diff --git a/mindspore/ccsrc/pipeline/jit/action.cc b/mindspore/ccsrc/pipeline/jit/action.cc index 686fc840b1f..6f081493b75 100644 --- a/mindspore/ccsrc/pipeline/jit/action.cc +++ b/mindspore/ccsrc/pipeline/jit/action.cc @@ -75,26 +75,44 @@ bool EnableTupleBroaden(const abstract::AbstractBasePtr &abs) { return abs->isa() && abs->cast()->ContainsAllBroadenTensors(); } +// Update the parameters of a function graph void UpdateFuncGraphParameter(const FuncGraphPtr &func_graph) { MS_EXCEPTION_IF_NULL(func_graph); + + // Initialize a vector to store the new parameters std::vector new_paras; + + // Iterate through the existing parameters of the function graph for (const auto ¶m : func_graph->parameters()) { + // Cast the parameter to a ParameterPtr auto param_node = param->cast(); MS_EXCEPTION_IF_NULL(param_node); + + // Check if the parameter has a default value if (param_node->has_default()) { + // If it has a default value, add it to the new parameters vector and continue to the next parameter new_paras.push_back(param_node); continue; } + + // Get the abstract value of the parameter AbstractBasePtr par_abs = param_node->abstract(); MS_EXCEPTION_IF_NULL(par_abs); + + // Check if the parameter's abstract type is undetermined, or if it can have any value, + // or if it is a scalar that should be enabled for gradient, or if it should be enabled for tuple broaden if (par_abs->isa() || par_abs->BuildValue() == kAnyValue || EnableGradForScalar(par_abs) || EnableTupleBroaden(par_abs)) { + // If any of the above conditions are met, add the parameter to the new parameters vector new_paras.push_back(param_node); } } + + // Set the parameters of the function graph to the new parameters vector func_graph->set_parameters(new_paras); } + bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) { MS_EXCEPTION_IF_NULL(func_graph); std::vector node_list = TopoSort(func_graph->get_return()); @@ -103,32 +121,57 @@ bool IsDynamicShapeGraph(const FuncGraphPtr &func_graph) { } // Disable mindRT in the heterogeneous scenario + dynamic_shape scenario. +// Disable the MindRT runtime in specific scenarios void DisableMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Get the current context auto context_ptr = MsContext::GetInstance(); MS_EXCEPTION_IF_NULL(context_ptr); + + // Check if MindRT is already disabled in the context, if so, return if (context_ptr->get_param(MS_CTX_ENABLE_MINDRT) == false) { return; } + + // Get the function graph from the resource auto func_graph = res->func_graph(); MS_EXCEPTION_IF_NULL(func_graph); + + // Get the parallel context auto parallel_context = parallel::ParallelContext::GetInstance(); MS_EXCEPTION_IF_NULL(parallel_context); + + // Get the current parallel mode auto parallel_mode = parallel_context->parallel_mode(); + + // Check if the parallel mode is SemiAutoParallel or AutoParallel bool is_parallel_mode = parallel_mode == parallel::kSemiAutoParallel || parallel_mode == parallel::kAutoParallel; + + // Check if the environment variable MS_DEV_ENABLE_CLOSURE is set to "0" bool enable_old_runtime = (common::GetEnv("MS_DEV_ENABLE_CLOSURE") == "0"); + + // Check if the function graph contains control flow nodes and is in parallel mode bool use_old_vm_for_control_parallel = func_graph->exist_multi_target() && ExistControlFlow(func_graph) && is_parallel_mode; + + // If any of the conditions for disabling MindRT are met, perform the following steps if (enable_old_runtime || use_old_vm_for_control_parallel) { - MS_LOG(INFO) << "Disable mindRT in the heterogeneous + control flow + parallel scenario."; + MS_LOG(INFO) << "Disable MindRT in the heterogeneous + control flow + parallel scenario."; + + // Set the ENABLE_MINDRT context parameter to false, effectively disabling MindRT context_ptr->set_param(MS_CTX_ENABLE_MINDRT, false); - // Update the backend. + + // Update the backend to use the old runtime auto new_backend = compile::CreateBackend(); new_backend->SetDebugger(); + + // Set the new backend as the result in the resource res->SetResult(kBackend, new_backend); } } + void TaskEmitActionForMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); // Get the mindRT backend. @@ -141,42 +184,58 @@ void TaskEmitActionForMindRT(const ResourcePtr &res) { res->SetResult(kOutput, actor_info); } +// Execute an action using MindRT void ExecuteActionForMindRT(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Retrieve the actor information from the resource const auto actor_info = res->GetResult(kOutput).cast(); - // Get the mindRT backend. + + // Get the MindRT backend from the resource std::shared_ptr bc_ptr = res->GetResult(kBackend).cast>(); auto mindrt_bc_ptr = (std::dynamic_pointer_cast(bc_ptr)).get(); MS_EXCEPTION_IF_NULL(mindrt_bc_ptr); - // Construct the graph run function ptr. + // Construct a graph run function pointer compile::VmEvalFuncPtr run = std::make_shared([mindrt_bc_ptr, actor_info](const VectorRef &args) -> BaseRef { MS_LOG(DEBUG) << "Execute args size " << args.size(); VectorRef outputs; + + // Run the graph using the MindRT backend mindrt_bc_ptr->RunGraph(actor_info, args, &outputs); + MS_LOG(DEBUG) << "out size " << outputs.size(); + + // Check if the outputs are empty, and return an empty VectorRef if they are if (outputs.empty()) { return VectorRef(); } else { + // Return the first element of the outputs return outputs[0]; } }); + + // Set the result in the resource with the key kOutput res->SetResult(kOutput, run); } + // Modify the output node of func_graph to add forward nodes used in bprop graph. +// Modify the output node of a function graph void ModifyOutputNode(const FuncGraphPtr &func_graph) { MS_EXCEPTION_IF_NULL(func_graph); + + // Get the set of forward nodes used in the function graph const auto &used_forward_nodes = func_graph->used_forward_nodes(); - // Get original output node and abstract + // Get the original output node and its abstract auto original_output_node = func_graph->output(); MS_EXCEPTION_IF_NULL(original_output_node); auto original_output_abs = original_output_node->abstract(); MS_EXCEPTION_IF_NULL(original_output_abs); - // Create a new make tuple node to hold all forward used nodes. + // Create a new make tuple node to hold all forward used nodes abstract::AbstractBasePtrList added_abs_list; std::vector added_node_list{NewValueNode(prim::kPrimMakeTuple)}; std::for_each(used_forward_nodes.begin(), used_forward_nodes.end(), @@ -185,30 +244,43 @@ void ModifyOutputNode(const FuncGraphPtr &func_graph) { added_node_list.push_back(node); added_abs_list.push_back(node->abstract()); }); + AnfNodePtr added_output_node = nullptr; AbstractBasePtr added_output_abs = nullptr; + + // If there are no forward used nodes, create a constant node with the value 1 if (added_abs_list.empty()) { added_output_node = NewValueNode(MakeValue(1)); added_output_abs = std::make_shared(std::make_shared(1)); } else { + // Create a new node that makes a tuple from the list of forward used nodes added_output_node = func_graph->NewCNode(std::move(added_node_list)); added_output_abs = std::make_shared(added_abs_list); } + + // Set the abstract for the added output node added_output_node->set_abstract(added_output_abs); MS_LOG(DEBUG) << "Added output node info: " << added_output_node->DebugString(); - // Merge original output node and used forward nodes to return node. + // Create a new make tuple node to merge the original output node and the added output node std::vector new_output_nodes{NewValueNode(prim::kPrimMakeTuple), original_output_node, added_output_node}; auto merge_node = func_graph->NewCNode(std::move(new_output_nodes)); + + // Create a new abstract that represents the merged output abstract::AbstractBasePtrList new_output_abs{original_output_abs, added_output_abs}; merge_node->set_abstract(std::make_shared(new_output_abs)); MS_LOG(DEBUG) << "Merge node info: " << merge_node->DebugString(); + + // Set the merge node as the new output of the function graph func_graph->set_output(merge_node); - // Clear + // Mark that the output has been modified func_graph->set_modify_output(true); + + // Clear the set of used forward nodes func_graph->ClearUsedForwardNodes(); } + } // namespace using CompileGraphs = compile::CompileGraphs; using abstract::AnalysisResult; @@ -260,161 +332,260 @@ abstract::AnalysisResult AbstractAnalyze(const ResourcePtr &resource, const Func return res; } +// Program specialization for a given function graph FuncGraphPtr ProgramSpecialize(const ResourcePtr &res, const FuncGraphPtr &func_graph, const abstract::AnalysisContextPtr &context) { MS_EXCEPTION_IF_NULL(res); + + // Log a debug message indicating the start of program specialization MS_LOG(DEBUG) << "ProgramSpecialize start"; + + // Create a program specializer using the engine from the resource abstract::ProgramSpecializer specializer(res->engine()); + + // Run the program specializer on the input function graph with the specified analysis context FuncGraphPtr result = specializer.Run(func_graph, context); + + // Get the manager from the resource auto manager = res->manager(); MS_EXCEPTION_IF_NULL(manager); + + // Keep the specialized result graph as a root in the manager manager->KeepRoots({result}); + + // Perform specialized handling for CNodes with input 0 as a function graph specializer.SpecializeCNodeInput0FuncGraph(); + + // Log a debug message indicating the end of program specialization MS_LOG(DEBUG) << "ProgramSpecialize end"; + + // Return the specialized result function graph return result; } + +// Renormalize a function graph with new arguments and return the specialized function graph FuncGraphPtr Renormalize(const ResourcePtr &res, const FuncGraphPtr &func_graph, const abstract::AbstractBasePtrList &args_spec) { MS_EXCEPTION_IF_NULL(res); + + // Log a debug message indicating the start of renormalization MS_LOG(DEBUG) << "Renormalize start"; + #ifdef ENABLE_PROFILE + // If profiling is enabled, record the start time double t1 = GetTime(); #endif + + // Analyze the function graph with the new arguments and return the analysis result abstract::AnalysisResult result = AbstractAnalyze(res, func_graph, args_spec, true); + #ifdef ENABLE_PROFILE + // If profiling is enabled, record the end time of analysis double t2 = GetTime(); #endif + + // Perform program specialization on the function graph using the analysis result auto ret = ProgramSpecialize(res, func_graph, result.context); + + // Set the specialized function graph as the new function graph in the resource res->set_func_graph(ret); + #ifdef ENABLE_PROFILE + // If profiling is enabled, record the time spent on analysis and specialization double t3 = GetTime(); MsProfile::StatTime("renormalize.infer", t2 - t1); MsProfile::StatTime("renormalize.specialize", t3 - t2); #endif + // Log a debug message indicating the end of renormalization MS_LOG(DEBUG) << "Renormalize end"; + // Return the specialized function graph return ret; } + +// Get the loaded graph from the manager in the resource const FuncGraphPtr GetLoadedGraph(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Get the manager from the resource auto manager = res->manager(); MS_EXCEPTION_IF_NULL(manager); + + // Initialize variables to store the loaded graph and count FuncGraphPtr loaded_graph = nullptr; size_t loaded_graph_num = 0; + + // Get all function graphs managed by the manager auto all_graphs = manager->func_graphs(); + + // Iterate through all function graphs for (auto &graph : all_graphs) { MS_EXCEPTION_IF_NULL(graph); + + // Check if the graph has an "is_load" attribute if (graph->has_attr("is_load")) { + // If the attribute is present, set the loaded graph and increment the count loaded_graph = graph; loaded_graph_num += 1; + + // Set the "is_load" flag in the resource to indicate that a loaded graph is found res->set_is_load(true); } } + + // If no loaded graph is found, return nullptr if (loaded_graph_num == 0) { return nullptr; } + + // If only one loaded graph is found, return it if (loaded_graph_num == 1) { return loaded_graph; } - MS_LOG(EXCEPTION) << "The loaded sub graph currently should be less than 2, but got " << loaded_graph_num; + + // If more than one loaded graph is found, log an exception with the count + MS_LOG(EXCEPTION) << "The loaded subgraph should be less than 2, but got " << loaded_graph_num; } + +// Check the shape and type of root graph inputs against the loaded graph inputs void CheckRootInputShapeAndType(const ResourcePtr &res, const FuncGraphPtr &loaded_graph) { MS_EXCEPTION_IF_NULL(res); auto manager = res->manager(); MS_EXCEPTION_IF_NULL(manager); + + // Get the root graph from the manager FuncGraphPtr root_graph = *(manager->roots().begin()); + + // Get inputs of both root and loaded graphs auto root_inputs = root_graph->get_inputs(); auto loaded_inputs = loaded_graph->get_inputs(); - MS_LOG(DEBUG) << "root_graph: " << root_graph->ToString(); - MS_LOG(DEBUG) << "loaded_graph: " << loaded_graph->ToString(); + + // Get the number of inputs for both graphs size_t root_inputs_num = root_inputs.size(); size_t loaded_inputs_num = loaded_inputs.size(); + + // Check if the number of inputs matches between the root and loaded graphs if (root_inputs_num != loaded_inputs_num) { - MS_LOG(EXCEPTION) << "The inputs number " << root_inputs_num << " not equal to the inputs number of loaded graph " - << loaded_inputs_num; + MS_LOG(EXCEPTION) << "The number of inputs in the root graph (" << root_inputs_num + << ") does not match the number of inputs in the loaded graph (" << loaded_inputs_num << ")"; } + // Iterate through the inputs and compare their shapes and types for (size_t index = 0; index < root_inputs_num; index++) { auto root_input = root_inputs[index]; auto loaded_input = loaded_inputs[index]; + // Debug log to display information about the inputs MS_LOG(DEBUG) << "root_input[" << index << "]: " << root_input->DebugString(1); MS_LOG(DEBUG) << "loaded_input[" << index << "]: " << loaded_input->DebugString(1); - MS_LOG(DEBUG) << "root_input abstract[" << index - << "]: " << (root_input->abstract() ? root_input->abstract()->ToString() : "NULL"); - MS_LOG(DEBUG) << "loaded_input abstract [" << index - << "]: " << (loaded_input->abstract() ? loaded_input->abstract()->ToString() : "NULL"); + MS_LOG(DEBUG) << "root_input abstract[" << index << "]: " << (root_input->abstract() ? root_input->abstract()->ToString() : "NULL"); + MS_LOG(DEBUG) << "loaded_input abstract[" << index << "]: " << (loaded_input->abstract() ? loaded_input->abstract()->ToString() : "NULL"); - auto root_shape = root_input->Shape() == nullptr ? nullptr : dyn_cast(root_input->Shape()); - auto loaded_shape = loaded_input->Shape() == nullptr ? nullptr : dyn_cast(loaded_input->Shape()); - auto root_type = root_input->Type() == nullptr ? nullptr : dyn_cast(root_input->Type()); - auto loaded_type = loaded_input->Type() == nullptr ? nullptr : dyn_cast(loaded_input->Type()); + // Get the shape and type of the inputs + auto root_shape = dyn_cast(root_input->Shape()); + auto loaded_shape = dyn_cast(loaded_input->Shape()); + auto root_type = dyn_cast(root_input->Type()); + auto loaded_type = dyn_cast(loaded_input->Type()); + // Check if the shape and type are not null MS_EXCEPTION_IF_NULL(root_shape); MS_EXCEPTION_IF_NULL(loaded_shape); MS_EXCEPTION_IF_NULL(root_type); MS_EXCEPTION_IF_NULL(loaded_type); - auto shapeEqu = (root_shape->shape() == loaded_shape->shape()) || - (root_shape->shape().size() <= 1 && loaded_shape->shape().size() <= 1); - if (!shapeEqu) { - MS_EXCEPTION(ValueError) << "The " << index - << " th input shape differ from loaded graph. Input shape: " << root_shape->ToString() - << ", input shape of loaded graph: " << loaded_shape->ToString(); + // Compare the shapes of the inputs, allowing for scalar inputs + auto shape_equal = (root_shape->shape() == loaded_shape->shape()) || + (root_shape->shape().size() <= 1 && loaded_shape->shape().size() <= 1); + + if (!shape_equal) { + MS_EXCEPTION(ValueError) << "The shape of the " << index << "th input differs between the root graph and the loaded graph." + << " Input shape: " << root_shape->ToString() << ", input shape of loaded graph: " << loaded_shape->ToString(); } + + // Compare the types of the inputs if (root_type->type_id() != loaded_type->type_id()) { - MS_EXCEPTION(TypeError) << "The " << std::to_string(index) - << " th input type differ from loaded graph. Input type: " << root_type->ToString() - << ", input type of loaded graph: " << loaded_type->ToString(); + MS_EXCEPTION(TypeError) << "The type of the " << index << "th input differs between the root graph and the loaded graph." + << " Input type: " << root_type->ToString() << ", input type of loaded graph: " << loaded_type->ToString(); } } } + +// Parse an action using the provided resource bool ParseAction(const ResourcePtr &res) { MS_EXCEPTION_IF_NULL(res); + + // Enable recording of debug information TraceManager::OpenRecordDebugInfoFlag(); + + // Check if the source input exists; if not, raise an exception if (!res->source_input()) { - MS_LOG(EXCEPTION) << "Parse error"; + MS_LOG(EXCEPTION) << "Parse error: Source input is null."; } + // Retrieve the source input py::object input = res->source_input(); + + // Initialize the parser environment with the input parse::Parser::InitParserEnvironment(input); + + // Import the 'os.path' module and retrieve the directory of the source file py::module path = py::module::import("os.path"); std::string dir = path.attr("dirname")(py::globals()["__file__"]).cast(); + // Set Python environment flags and update Python path python_adapter::set_python_env_flag(true); python_adapter::SetPythonPath(dir); + // Convert the input data to a ValuePtr ValuePtr converted_ret = nullptr; bool converted = parse::ConvertData(input, &converted_ret, true); + + // Check if the conversion was successful; if not, raise an exception if (!converted) { - MS_LOG(EXCEPTION) << "Attribute convert error with type:" << std::string(py::str(input)); + MS_LOG(EXCEPTION) << "Attribute convert error with type: " << std::string(py::str(input)); } FuncGraphPtr top_graph = nullptr; + + // Check if the input is an instance of Cell if (py::isinstance(input)) { + // If it is a Cell, create a top-level graph using the input and converted result top_graph = parse::MakeTopGraph(input, converted_ret); } else if (converted_ret->isa()) { + // If the converted result is a FuncGraph, use it as the top-level graph top_graph = converted_ret->cast(); } else { - MS_LOG(EXCEPTION) << "Object to parse " << std::string(py::str(input)) << " is not function or cell."; + // If the input is neither a Cell nor a FuncGraph, raise an exception + MS_LOG(EXCEPTION) << "Object to parse " << std::string(py::str(input)) << " is not a function or cell."; } + + // Update the top-level FuncGraph in the parser parse::Parser::UpdateTopFuncGraph(top_graph); + // Set the top-level FuncGraph in the resource res->set_func_graph(top_graph); + // Retrieve the FuncGraph manager from the resource FuncGraphManagerPtr manager = res->manager(); + + // Check if the manager is not null; if it is null, raise an exception if (manager == nullptr) { MS_LOG(EXCEPTION) << "Manager is nullptr."; } + + // Add the top-level FuncGraph to the manager manager->AddFuncGraph(top_graph); + + // Return true to indicate successful parsing return true; } + // obj_map's graphs have the same construct, these graphs can be optimized to one graph. // This step do this optimize: graph1(x){xx(fv1),xxx(fv2)}, graph2(x){xxx(fv3),xxx(fv4)}-> // graph1(x){base_graph(x, fv1, fv2)}, graph1(x){base_graph(x, fv3, fv4)}, base_graph(x, fv...){xxx,xxx} diff --git a/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.cc b/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.cc index 4eb6bb34d3a..c255a12de9d 100644 --- a/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.cc +++ b/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.cc @@ -48,123 +48,228 @@ using mindspore::tensor::TensorPy; namespace mindspore { namespace { +// Definition of a structure called "AnfDumpHandlerRegister" struct AnfDumpHandlerRegister { + // Constructor for AnfDumpHandlerRegister AnfDumpHandlerRegister() { + // Within the constructor, a lambda function is used as an argument to SetDumpDatHandler. AnfDumpHandler::SetDumpDatHandler([](const std::string &realpath, const FuncGraphPtr &graph) { + // Inside the lambda function: + + // Create an AnfExporter object named "exporter" with an empty string as a parameter. AnfExporter exporter(""); + + // Append ".dat" to the "realpath" string to create the output file path. std::string realpath_dat = realpath + ".dat"; + + // Change file permissions for the "realpath_dat" file to allow read, write, and execute for the owner. ChangeFileMode(realpath_dat, S_IRWXU); + + // Export the given FuncGraph "graph" to the "realpath_dat" file using the "exporter" object. exporter.ExportFuncGraph(realpath_dat, graph); + + // Change file permissions for the "realpath_dat" file to allow read only for the owner. ChangeFileMode(realpath_dat, S_IRUSR); }); } -} callback_register; +} callback_register; // An instance of AnfDumpHandlerRegister is created and named "callback_register." } // namespace + // ============================================= MindSpore IR Exporter ============================================= + +// Definition of a member function named "GetNodeType" belonging to the AnfExporter class. std::string AnfExporter::GetNodeType(const AnfNodePtr &nd) { MS_EXCEPTION_IF_NULL(nd); + + // Initialize a nullptr ValuePtr called "tensor_value." ValuePtr tensor_value = nullptr; + + // Get the abstract property of the given AnfNode "nd." auto abstract = nd->abstract(); + + // Check if the abstract is not null and is an instance of AbstractTensor. if (abstract != nullptr && abstract->isa()) { + // Build the ValuePtr from the abstract. tensor_value = abstract->BuildValue(); } + + // Convert the Shape property of the node into a ShapePtr, if it exists. abstract::ShapePtr shape = nd->Shape() == nullptr ? nullptr : dyn_cast(nd->Shape()); + + // Convert the Type property of the node into a TypePtr. TypePtr type = dyn_cast(nd->Type()); + + // Initialize a string stream to construct the output string. std::ostringstream oss; + + // Check if both shape and type are not nullptr. if ((shape != nullptr) && (type != nullptr)) { + // Concatenate the type's DumpText and shape's DumpText into the output string. oss << type->DumpText() << shape->DumpText(); + + // Check if tensor_value is not nullptr and not kAnyValue. if (tensor_value != nullptr && tensor_value != kAnyValue) { + // Append "(...)" to the output string. oss << "(...)"; } } else if (type != nullptr) { + // Concatenate only the type's DumpText into the output string. oss << type->DumpText(); + + // Check if tensor_value is not nullptr and not kAnyValue. if (tensor_value != nullptr && tensor_value != kAnyValue) { + // Append "(...)" to the output string. oss << "(...)"; } } else { + // If neither shape nor type is available, set the output string to "Undefined." oss << "Undefined"; } + + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "GetParamIndex" belonging to the AnfExporter class. int AnfExporter::GetParamIndex(const FuncGraphPtr &func_graph, const AnfNodePtr ¶m, bool throw_excp) { + // Check if either the func_graph or param is nullptr. if (func_graph == nullptr || param == nullptr) { + // If either is nullptr, return -1 to indicate an invalid index. return -1; } + // Initialize a FuncGraphPtr called "fg" and set it to the input func_graph. FuncGraphPtr fg = func_graph; + + // Loop until "fg" becomes nullptr. while (fg != nullptr) { + // Check if "fg" is not found in the "exported" set. if (exported.find(fg) == exported.end()) { + // If "fg" is not found and check_integrity_ is not enabled, break out of the loop. if (!check_integrity_) { break; } + // Log an exception message indicating that the func graph is not found. MS_LOG(EXCEPTION) << "Can not find func graph '" << fg->DumpText() << "'"; } + + // Retrieve the parameter map associated with "fg." auto param_map = exported[fg]; + + // Check if "param" is found in the parameter map. if (param_map.find(param) != param_map.end()) { + // If found, return the index associated with "param" in the parameter map. return param_map[param]; } + + // Move to the parent func graph. fg = fg->parent(); } + + // If throw_excp is true, log an exception message indicating that the parameter index could not be found. if (throw_excp) { MS_LOG(EXCEPTION) << "Can not find index for param '" << param->DumpText() << "' for func graph '" << func_graph->DumpText() << "'"; } + + // Return -1 to indicate that the parameter index could not be found. return -1; } -// Try to find index of parameter for SymbolicKeyInstance from all exported graphs -// NOTICE: Suppose name of all parameters in SymbolicKeyInstance are different + +// Try to find the index of a parameter for SymbolicKeyInstance from all exported graphs +// NOTICE: Suppose the names of all parameters in SymbolicKeyInstance are different + +// Definition of a member function named "GetParamIndexFromExported" belonging to the AnfExporter class. int AnfExporter::GetParamIndexFromExported(const AnfNodePtr ¶m) { + // Check if the input parameter "param" is nullptr. if (param == nullptr) { + // If "param" is nullptr, return -1 to indicate an invalid index. return -1; } + // Initialize the return value "ret" to -1. int ret = -1; + + // Loop through each item in the "exported" container (presumably a map or a similar data structure). for (const auto &item : exported) { - auto pram_iter = item.second.find(param); - if (pram_iter != item.second.end()) { - return pram_iter->second; + // Attempt to find "param" in the parameter map associated with the current item. + auto param_iter = item.second.find(param); + + // Check if "param" was found in the parameter map. + if (param_iter != item.second.end()) { + // If found, return the index associated with "param" in the parameter map. + return param_iter->second; } } + + // If the loop completes without finding "param" in any of the exported graphs, return the initial value of "ret" (-1). return ret; } + std::string AnfExporter::GetValueNodeText(const FuncGraphPtr &fg, const ValueNodePtr &node) { MS_EXCEPTION_IF_NULL(node); return GetValueText(fg, node->value()); } +// Definition of a member function named "GetMultitypeFuncGraphText" belonging to the AnfExporter class. std::string AnfExporter::GetMultitypeFuncGraphText(const prim::MultitypeFuncGraphPtr &mt_func_graph) { + // Retrieve a vector of PyFunctions from the MultitypeFuncGraph. auto py_funcs = mt_func_graph->GetPyFunctions(); + + // Check if the vector of PyFunctions is empty. if (py_funcs.empty()) { + // If empty, return an empty string. return ""; } + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + // Start constructing the output string with an opening curly brace. oss << "{"; + + // Initialize a boolean flag "is_first" to track the first PyFunction. bool is_first = true; + + // Iterate through each PyFunction in the vector. for (const auto &py_func : py_funcs) { + // Check if it's the first PyFunction. if (is_first) { is_first = false; } else { + // Add a comma and a space to separate PyFunctions. oss << ", "; } + + // Add an opening parenthesis for the PyFunction arguments. oss << "("; + + // Iterate through the PyFunction's arguments. for (size_t i = 0; i < py_func.first.size(); ++i) { + // Add a comma and a space to separate arguments (if not the first argument). if (i > 0) { oss << ", "; } + + // Add the textual representation of the PyFunction's argument to the output string. oss << py_func.first[i]->DumpText(); } + + // Add a closing parenthesis for the PyFunction arguments. oss << ")"; } + + // Add a closing curly brace to complete the output string. oss << "}"; + // Return the constructed output string. 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() || @@ -187,75 +292,110 @@ inline bool Skip(const MetaFuncGraphPtr &meta_func_graph) { * ├── GradOperation * └── TupleAdd */ +// Definition of a member function named "GetMetaFuncGraphText" belonging to the AnfExporter class. std::string AnfExporter::GetMetaFuncGraphText(const MetaFuncGraphPtr &meta_func_graph) { + // Check if the input meta_func_graph is nullptr. if (meta_func_graph == nullptr) { + // If nullptr, return an empty string. return ""; } + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + + // Append the type name and name of the meta_func_graph to the output string. oss << meta_func_graph->type_name() << "::" << meta_func_graph->name(); + // Check the type of the meta_func_graph. if (meta_func_graph->isa()) { + // If it's a MultitypeFuncGraph, cast it and append its text representation to the output string. 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()' + } else if (meta_func_graph->isa()) { // Check if it's a HyperMapPy. auto hyper_map = meta_func_graph->cast(); if (hyper_map->GetFnLeaf() != nullptr) { + // If it has a function leaf, append its text representation to the output string. oss << "{fn_leaf=" << GetMetaFuncGraphText(hyper_map->GetFnLeaf()) << "}"; } - } else if (meta_func_graph->isa()) { + } else if (meta_func_graph->isa()) { // Check if it's a HyperMap. auto hyper_map = meta_func_graph->cast(); if (hyper_map->GetFnLeaf() != nullptr) { + // If it has a function leaf, append its text representation to the output string. oss << "{fn_leaf=" << GetMetaFuncGraphText(hyper_map->GetFnLeaf()) << "}"; } - } else if (meta_func_graph->isa()) { // This statement must before 'meta_graph->isa()' + } else if (meta_func_graph->isa()) { // Check if it's a MapPy. auto map = meta_func_graph->cast(); if (map->GetFnLeaf() != nullptr) { + // If it has a function leaf, append its text representation to the output string. oss << "{fn_leaf=" << GetMetaFuncGraphText(map->GetFnLeaf()) << "}"; } - } else if (meta_func_graph->isa()) { + } else if (meta_func_graph->isa()) { // Check if it's a Map. auto map = meta_func_graph->cast(); if (map->GetFnLeaf() != nullptr) { + // If it has a function leaf, append its text representation to the output string. oss << "{fn_leaf=" << GetMetaFuncGraphText(map->GetFnLeaf()) << "}"; } - } else if (meta_func_graph->isa()) { + } else if (meta_func_graph->isa()) { // Check if it's a GradOperation. prim::GradOperationPtr grad_op = meta_func_graph->cast(); + // Append information about GradOperation to the output string. oss << "{get_all=" << grad_op->get_all_ << ", get_by_list=" << grad_op->get_by_list_ << ", sens_param=" << grad_op->sens_param_ << "}"; } else if (meta_func_graph->isa() || meta_func_graph->isa() || Skip(meta_func_graph)) { - // Do nothing. + // Do nothing for certain types of meta_func_graphs. } else { + // If the type is not recognized, log an exception with the type name. MS_LOG(EXCEPTION) << "Unknown MetaFuncGraph type " << meta_func_graph->type_name(); } + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "GetPrimitiveText" belonging to the AnfExporter class. std::string AnfExporter::GetPrimitiveText(const PrimitivePtr &prim) { + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + + // Check if the input primitive pointer "prim" is nullptr. if (prim == nullptr) { + // If it's nullptr, return an empty string. return oss.str(); } + + // Append the type name and name of the primitive to the output string. oss << prim->type_name() << "::" << prim->name(); - // Output primitive type + + // Output the primitive type as an integer. oss << "{prim_type=" << static_cast(prim->prim_type()) << "}"; - // Output primitive attributes + + // Output the primitive attributes by calling the GetAttrsText method of the primitive. oss << prim->GetAttrsText(); + // Check if the primitive is of type "prim::DoSignaturePrimitive." if (prim->isa()) { + // Cast it to the appropriate type. auto do_signature = dyn_cast(prim); + + // Get the function associated with the DoSignaturePrimitive. auto &func = do_signature->function(); + + // Check if the function is of type "Primitive." if (func->isa()) { + // Cast the function to a Primitive type. auto sig_prim = dyn_cast(func); + + // Output the attributes of the signature primitive. oss << sig_prim->GetAttrsText(); } } + // Return the constructed output string. return oss.str(); } + std::string AnfExporter::GetNameSpaceText(const parse::NameSpacePtr &ns) { std::ostringstream oss; if (ns == nullptr) { @@ -268,20 +408,33 @@ std::string AnfExporter::GetNameSpaceText(const parse::NameSpacePtr &ns) { return oss.str(); } +// Definition of a member function named "GetSymbolicKeyInstanceText" belonging to the AnfExporter class. std::string AnfExporter::GetSymbolicKeyInstanceText(const FuncGraphPtr &func_graph, const SymbolicKeyInstancePtr &sym_inst) { + // Check for null pointers for func_graph and sym_inst. MS_EXCEPTION_IF_NULL(func_graph); MS_EXCEPTION_IF_NULL(sym_inst); + + // Get the underlying AnfNodePtr from the SymbolicKeyInstance. AnfNodePtr sym_node = sym_inst->node(); + + // Check for null pointer for sym_node. MS_EXCEPTION_IF_NULL(sym_node); + + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + + // Check if the sym_node is of type Parameter. if (sym_node->isa()) { + // Attempt to get the index of the Parameter in the ancestor FuncGraph. 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 the index is negative, try to find the Parameter in all exported graphs. if (idx < 0) { idx = GetParamIndexFromExported(sym_node); } + + // If the index is still negative, log a warning. if (idx < 0) { ParameterPtr p = dyn_cast(sym_node); if (p == nullptr) { @@ -289,178 +442,314 @@ std::string AnfExporter::GetSymbolicKeyInstanceText(const FuncGraphPtr &func_gra } MS_LOG(WARNING) << "Can not find SymbolicKeyInstance: " << p->name(); } + + // Append the text representation of the SymbolicKeyInstance to the output string. oss << "SymInst(%para" << idx << ")"; } else { + // If sym_node is not a Parameter, log a warning and append its text representation to the output string. MS_LOG(WARNING) << "SymbolicKeyInstance does not embed a parameter: " << sym_node->ToString(); oss << "SymInst(cnode_" << sym_node->ToString() << ")"; } + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "GetSequenceText" belonging to the AnfExporter class. std::string AnfExporter::GetSequenceText(const FuncGraphPtr &func_graph, const ValuePtr &value) { + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; - // Output ValueList, ValueTuple + + // Check if the input "value" is a ValueSequence. ValueSequencePtr seq = dyn_cast(value); MS_EXCEPTION_IF_NULL(seq); + + // Check if the input "value" is not null. MS_EXCEPTION_IF_NULL(value); + + // Determine if the sequence is a tuple based on the type of "value." bool is_tuple = value->isa(); + + // Start constructing the output string with an opening parenthesis for tuples or an opening square bracket for lists. oss << (is_tuple ? "(" : "["); + + // Initialize a flag to track if it's the first element in the sequence. bool first_flag = true; + + // Iterate through each element in the sequence. for (auto elem : seq->value()) { + // Check if it's the first element. if (first_flag) { first_flag = false; } else { + // Add a comma and a space to separate elements (if not the first element). oss << ", "; } + + // Append the text representation of the element to the output string. oss << GetValueText(func_graph, elem); } + + // Add a closing parenthesis for tuples or a closing square bracket for lists to complete the output string. oss << (is_tuple ? ")" : "]"); + + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "GetDictText" belonging to the AnfExporter class. std::string AnfExporter::GetDictText(const FuncGraphPtr &func_graph, const ValuePtr &value) { + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + + // Attempt to cast the input "value" to a ValueDictionary. ValueDictionaryPtr dict = value->cast(); + + // Start constructing the output string with an opening curly brace. oss << "{"; + + // Initialize a flag to track if it's the first element in the dictionary. bool first_flag = true; + + // Iterate through each key-value pair in the dictionary. for (const auto &elem : dict->value()) { + // Check if it's the first key-value pair. if (first_flag) { first_flag = false; } else { + // Add a comma and a space to separate key-value pairs (if not the first pair). oss << ", "; } - oss << "\"" << elem.first << "\": " << GetValueText(func_graph, elem.second); + + // Enclose the key in double quotes and append it to the output string. + oss << "\"" << elem.first << "\": "; + + // Append the text representation of the value associated with the key to the output string. + oss << GetValueText(func_graph, elem.second); } + + // Add a closing curly brace to complete the output string. oss << "}"; + + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "GetOtherValueText" belonging to the AnfExporter class. std::string AnfExporter::GetOtherValueText(const FuncGraphPtr &, const ValuePtr &value) { + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + // Check if check_integrity_ is enabled. if (check_integrity_) { + // If enabled, log an exception indicating that processing is needed for this type and dump text. MS_LOG(EXCEPTION) << "Need to process type: " << value->type_name() << ", dump text: " << value->DumpText(); } + + // Append the type name and dump text of the value to the output string. oss << value->type_name() << "[" << value->DumpText() << "]"; + // Return the constructed output string. return oss.str(); } +// Definition of a static helper function named "CanUseDumpText." static bool CanUseDumpText(const ValuePtr &value) { + // Check if the value is one of the supported types for DumpText. return (value->isa() || value->isa() || value->isa() || value->isa() || value->isa() || value->isa() || value->isa() || value->isa() || value->isa() || value->isa()); } + +// Definition of a member function named "GetValueText" belonging to the AnfExporter class. std::string AnfExporter::GetValueText(const FuncGraphPtr &func_graph, const ValuePtr &value) { + // Check if either the func_graph or the value is nullptr. if (func_graph == nullptr || value == nullptr) { + // If either is nullptr, return an empty string. return ""; } + + // Check the type of the value and generate text representation accordingly. + + // Check if the value is of type Primitive. if (value->isa()) { + // If so, call the GetPrimitiveText function to generate its text representation. return GetPrimitiveText(value->cast()); } + + // Check if the value is of type MetaFuncGraph. if (value->isa()) { + // If so, call the GetMetaFuncGraphText function to generate its text representation. MetaFuncGraphPtr meta_func_graph = value->cast(); return GetMetaFuncGraphText(meta_func_graph); } + + // Check if the value is of type SymbolicKeyInstance. if (value->isa()) { + // If so, call the GetSymbolicKeyInstanceText function to generate its text representation. return GetSymbolicKeyInstanceText(func_graph, value->cast()); } + + // Check if the value is of type ValueSequence. if (value->isa()) { + // If so, call the GetSequenceText function to generate its text representation. return GetSequenceText(func_graph, value); } + + // Check if the value is of type ValueDictionary. if (value->isa()) { + // If so, call the GetDictText function to generate its text representation. return GetDictText(func_graph, value); } + + // Check if the value is of type parse::NameSpace. if (value->isa()) { + // If so, call the GetNameSpaceText function to generate its text representation. return GetNameSpaceText(value->cast()); } + + // Check if the value is of type parse::PyObjectWrapper. if (value->isa()) { + // If so, return the type name of the value. return value->type_name(); } + + // Check if the value can use DumpText based on the CanUseDumpText helper function. if (CanUseDumpText(value)) { + // If so, return the result of calling DumpText on the value. return value->DumpText(); } + + // If none of the above conditions match, call GetOtherValueText recursively to handle other value types. return GetOtherValueText(func_graph, value); } -// This function is used to output node in CNode's inputs + +// Definition of a member function named "GetAnfNodeText" belonging to the AnfExporter class. std::string AnfExporter::GetAnfNodeText(const FuncGraphPtr &func_graph, const AnfNodePtr &node, const std::map &apply_map) { + // Initialize a string stream "oss" to construct the output string. std::ostringstream oss; + + // Check if either the func_graph or the node is nullptr. if (func_graph == nullptr || node == nullptr) { + // If either is nullptr, return an empty string. return oss.str(); } + // Determine the type of the AnfNode and generate text representation accordingly. + + // Check if the node is of type CNode. if (node->isa()) { + // If it's a CNode, look up its corresponding index in the apply_map. auto iter = apply_map.find(node); + + // If not found in apply_map, log an exception. if (iter == apply_map.end()) { MS_LOG(EXCEPTION) << "Can not find node '" << node->DumpText() << "' in apply_map"; } + + // Append the index to the output string. oss << "%" << iter->second; } else if (node->isa()) { - // Parameter maybe a free variable, so check it in its own funcgraph. + // If it's a Parameter, it may be a free variable, so check it in its own funcgraph. oss << "%para" << GetParamIndex(node->func_graph(), node, check_integrity_); } else if (IsValueNode(node)) { + // Check if the node is a ValueNode containing a FuncGraph. FuncGraphPtr fg = GetValueNode(node); + + // Append the type name and unique identifier of the FuncGraph to the output string. oss << fg->type_name() << "::fg_" << fg->debug_info()->get_id(); + // Check if the FuncGraph is not already processed and should be exported. if (!func_graph_set.contains(fg) && exported.find(fg) == exported.end() && export_used_) { func_graph_set.add(fg); } } else if (node->isa()) { + // If it's a ValueNode, call GetValueNodeText to generate its text representation. oss << GetValueNodeText(func_graph, node->cast()); } else { + // If the node type is unknown, log an exception. MS_LOG(EXCEPTION) << "Unknown node '" << node->DumpText() << "'"; } + // Return the constructed output string. return oss.str(); } + +// Definition of a member function named "OutputParameters" belonging to the AnfExporter class. void AnfExporter::OutputParameters(std::ostringstream &oss, const std::vector ¶meters, ParamIndexMap *param_map) { + // Initialize a flag to track if it's the first parameter. bool first_flag = true; + + // Iterate through each parameter in the list of parameters. for (const AnfNodePtr ¶m : parameters) { + // Check if it's the first parameter. if (first_flag) { first_flag = false; + + // Add indentation for the first parameter. oss << " "; } else { + // Add a comma and space to separate parameters (if not the first parameter). oss << " , "; } + + // Add the parameter to the param_map with its corresponding param_index. (*param_map)[param] = param_index; + + // Get the type information for the parameter using the GetNodeType function. std::string type_info = GetNodeType(param); - // Output parameter and type + + // Output the parameter and its type information. if (type_info == "Undefined") { + // If the type_info is "Undefined," only output the parameter name with param_index. oss << "%para" << param_index; } else { + // Otherwise, output the parameter name with param_index and its type information. oss << "%para" << param_index << " : " << type_info; } - // Output comment + + // Output a comment with the parameter's dump text. oss << " # " << param->DumpText() << "\n"; + + // Increment the param_index for the next parameter. param_index += 1; } } + +// Definition of a member function named "OutputStatementComment" belonging to the AnfExporter class. void AnfExporter::OutputStatementComment(std::ostringstream &oss, const CNodePtr &node) { + // Check if the input node is nullptr. if (node == nullptr) { return; } - // Output type of each input argument + // Output type information of each input argument. auto &inputs = node->inputs(); if (inputs.size() > 1) { + // If there are more than one input arguments, start a comment with "(" to indicate input argument types. oss << " #("; for (size_t i = 1; i < inputs.size(); ++i) { if (i != 1) { oss << ", "; } AnfNodePtr arg = inputs[i]; + // Get the type information for the input argument using the GetNodeType function. oss << GetNodeType(arg); } + // Close the comment with ")" to indicate the end of input argument types. oss << ")"; } - // Output other comment, map the graph name to original representation(containing unicode character) + + // Output additional comment information, mapping the graph name to its original representation. std::ostringstream comment; comment << " #"; bool has_comment = false; @@ -478,31 +767,52 @@ void AnfExporter::OutputStatementComment(std::ostringstream &oss, const CNodePtr auto func_graph_id = fg->debug_info()->get_id(); comment << " fg_" << func_graph_id << "=" << fg->ToString(); } + // Append the additional comment to the output stream if there is any. if (has_comment) { oss << comment.str(); } + + // Output a comment indicating the scope of the node. oss << " #scope: " << node->scope()->name(); } + +// Definition of a member function named "OutputCNodeText" belonging to the AnfExporter class. void AnfExporter::OutputCNodeText(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph, int *idx, std::map *const apply_map) { + // Check if any of the required parameters is nullptr. if (cnode == nullptr || func_graph == nullptr || idx == nullptr || apply_map == nullptr) { return; } + + // Retrieve the inputs of the CNode. auto &inputs = cnode->inputs(); + + // Get text representation of the operation (first input). std::string op_text = GetAnfNodeText(func_graph, inputs[0], *apply_map); + + // Determine if this CNode belongs to a different function graph and set fv_text accordingly. std::string fv_text = (cnode->func_graph() != func_graph) ? ("$(" + cnode->func_graph()->ToString() + "):") : ""; - // Non-return node + + // Check if this CNode is not the return node. if (cnode != func_graph->get_return()) { int apply_idx = (*idx)++; (*apply_map)[cnode] = apply_idx; + + // Get type information for the CNode. std::string type_info = GetNodeType(cnode); + + // Get the function string if available. std::string func_str = GetNodeFuncStr(inputs[0]); + + // Start building the output line for the CNode. if (type_info == "Undefined") { oss << " %" << apply_idx << " = " << fv_text << op_text; } else { oss << " %" << apply_idx << " : " << fv_text << type_info << " = " << op_text; } + + // Add function string (if available) and opening parenthesis. if (!func_str.empty()) { oss << "[" << func_str << "]" << "("; @@ -510,9 +820,11 @@ void AnfExporter::OutputCNodeText(std::ostringstream &oss, const CNodePtr &cnode oss << "("; } } else { + // This CNode is the return node, so build the return statement line. oss << " " << fv_text << op_text << "("; } + // Add the input arguments to the output line. for (size_t i = 1; i < inputs.size(); ++i) { if (i != 1) { oss << ", "; @@ -520,9 +832,12 @@ void AnfExporter::OutputCNodeText(std::ostringstream &oss, const CNodePtr &cnode AnfNodePtr arg = inputs[i]; oss << GetAnfNodeText(func_graph, arg, *apply_map); } + + // Close the function call with a closing parenthesis. oss << ")"; } + void AnfExporter::OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph, int *idx, std::map *const apply_map) { OutputCNodeText(oss, cnode, func_graph, idx, apply_map); @@ -531,20 +846,30 @@ void AnfExporter::OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, co oss << "\n"; } +// Definition of a member function named "OutputCNodes" belonging to the AnfExporter class. void AnfExporter::OutputCNodes(std::ostringstream &oss, const std::vector &nodes, const FuncGraphPtr &func_graph, const TaggedNodeMap &tagged_cnodes_map) { + // Check if the func_graph is nullptr. if (func_graph == nullptr) { return; } - MS_LOG_TRY_CATCH_SCOPE; + + // Initialize the index for Apply nodes to start at 1. int idx = 1; + + // Create a map to keep track of Apply nodes and their indices. std::map apply_map; + + // Iterate through each AnfNode in the list of nodes. for (const AnfNodePtr &node : nodes) { MS_EXCEPTION_IF_NULL(node); + + // Check if the node is a CNode (Computation Node). if (!node->isa()) { - continue; + continue; // Skip non-CNode nodes. } + // Check if there are tagged CNodes and add a comment separator if needed. if (!tagged_cnodes_map.empty()) { auto iter = tagged_cnodes_map.find(node); if (iter != tagged_cnodes_map.end()) { @@ -552,8 +877,13 @@ void AnfExporter::OutputCNodes(std::ostringstream &oss, const std::vectorcast(); - OutputCNode(oss, cnode, func_graph, &idx, &apply_map); + + // Output the text representation of the CNode. + OutputCNodeText(oss, cnode, func_graph, &idx, &apply_map); + + // Output debug information for the CNode, including source line and label information. if (label_manage::GetGlobalTraceLabelType() == label_manage::TraceLabelType::kWithUniqueId) { oss << trace::GetDebugInfo(cnode->debug_info(), " # ", kSourceLineTipDiscard) << "#" << label_manage::Label(cnode->debug_info()) << "\n"; @@ -564,115 +894,205 @@ void AnfExporter::OutputCNodes(std::ostringstream &oss, const std::vectororder_list(); + + // Check if the order list is empty. if (order_list.empty()) { return; } + + // Set the width for formatting the node index. constexpr int width = 4; + + // Output a comment to indicate the start of order information. oss << "# order:\n"; + + // Initialize an index to track the order of nodes. int i = 1; + + // Iterate through the nodes in the order list. for (auto &node : order_list) { + // Output a comment with the node index (formatted), node's DebugString, and a newline character. oss << '#' << std::setw(width) << i << ": " << node->DebugString() << '\n'; + + // Increment the index for the next node. ++i; } } +// Definition of a member function named "ExportOneFuncGraph" belonging to the AnfExporter class. void AnfExporter::ExportOneFuncGraph(std::ostringstream &oss, const FuncGraphPtr &func_graph, const TaggedNodeMap &tagged_cnodes_map) { + // Check if the func_graph is nullptr. if (func_graph == nullptr) { return; } + // Perform a topological sort of nodes within the FuncGraph. std::vector nodes = TopoSort(func_graph->get_return(), SuccIncoming, AlwaysInclude); + + // Retrieve the list of parameters for the FuncGraph. std::vector parameters = func_graph->parameters(); + + // Create a map to store the parameter index. ParamIndexMap param_map; + // Output information about switch inputs if they exist. if (*(func_graph->switch_input())) { oss << "switch_input: " << *(func_graph->switch_input()) << "\n"; } if (*(func_graph->switch_layer_input())) { oss << "switch_layer_input: " << *(func_graph->switch_layer_input()) << "\n"; } + + // Output a comment with the FuncGraph's index and its DumpText. oss << "# [No." << (exported.size() + 1) << "] " << func_graph->DumpText() << "\n"; + + // Output debug information for the FuncGraph, including source line and label information. if (label_manage::GetGlobalTraceLabelType() == label_manage::TraceLabelType::kWithUniqueId) { oss << trace::GetDebugInfo(func_graph->debug_info(), "# ", kSourceLineTipDiscard) << "#" << label_manage::Label(func_graph->debug_info()) << "\n"; } else { oss << trace::GetDebugInfo(func_graph->debug_info(), "# ", kSourceLineTipDiscard) << "\n"; } + + // Output the header of the FuncGraph, including its name and parent's name if it exists. oss << "funcgraph fg_" << func_graph->debug_info()->get_id(); - // Output name of parent of graph if exists if (func_graph->parent() != nullptr) { oss << "[fg_" << func_graph->parent()->debug_info()->get_id() << "]"; } oss << "(\n"; + // Output the parameters of the FuncGraph. OutputParameters(oss, parameters, ¶m_map); + // Store the parameter map in the exported map for later reference. exported[func_graph] = param_map; + + // Output the opening curly brace for the FuncGraph's definition. oss << (!parameters.empty() ? " " : "") << ") {\n"; + // Output the CNodes (Computation Nodes) within the FuncGraph. OutputCNodes(oss, nodes, func_graph, tagged_cnodes_map); + // Output the closing curly brace for the FuncGraph's definition. oss << "}\n"; + // Output the order list of nodes within the FuncGraph. OutputOrderList(oss, func_graph); } + +// Definition of a member function named "ExportFuncGraph" belonging to the AnfExporter class. void AnfExporter::ExportFuncGraph(const std::string &filename, const FuncGraphPtr &func_graph) { + // Check if the func_graph is nullptr. if (func_graph == nullptr) { return; } + // Open the specified file for writing. std::ofstream ofs(filename); + + // Check if the file was opened successfully. if (!ofs.is_open()) { MS_LOG(ERROR) << "Open file '" << filename << "' failed!" << ErrnoToString(errno); return; } + // Initialize the parameter index. param_index = 1; + + // Create an output stream buffer to hold the exported code. std::ostringstream buffer; + + // Create a map to store tagged CNodes for later reference. TaggedNodeMap tagged_cnodes_map; + + // Add the initial func_graph to the set of func_graphs to be processed. func_graph_set.add(func_graph); + + // Process each func_graph in the set. while (!func_graph_set.empty()) { FuncGraphPtr fg = *func_graph_set.begin(); + + // Export the text representation of the current func_graph. ExportOneFuncGraph(buffer, fg, tagged_cnodes_map); + + // Add newline characters to separate func_graphs. buffer << "\n\n"; + + // Remove the processed func_graph from the set. (void)func_graph_set.erase(fg); } + + // Output the total number of function graphs exported. buffer << "# num of total function graphs: " << exported.size(); + + // Write the exported code to the output file. ofs << buffer.str(); + + // Close the output file. ofs.close(); } -#ifdef ENABLE_DUMP_IR + +// Function definition for ExportIR void ExportIR(const std::string &filename, const FuncGraphPtr &func_graph) { + // Check if the func_graph is nullptr. if (func_graph == nullptr) { return; } + // Generate a file path for saving the IR graph. auto filepath = GetSaveGraphsPathName(Common::AddId(filename, ".dat")); + + // Create the real file path with proper prefix. auto real_filepath = Common::CreatePrefixPath(filepath); + + // Check if the real file path is valid. if (!real_filepath.has_value()) { - MS_LOG(ERROR) << "The export ir path: " << filepath << " is not illegal."; + MS_LOG(ERROR) << "The export ir path: " << filepath << " is not legal."; return; } + + // Change file mode to allow writing by the user. ChangeFileMode(real_filepath.value(), S_IWUSR); + + // Create an instance of the AnfExporter class. AnfExporter exporter; + + // Export the FuncGraph to the specified file. exporter.ExportFuncGraph(real_filepath.value(), func_graph); - // Set file mode to read only by user + + // Set file mode to read-only for the user. ChangeFileMode(real_filepath.value(), S_IRUSR); } + +// Conditional compilation based on ENABLE_DUMP_IR +#ifdef ENABLE_DUMP_IR +} // namespace mindspore #else +// Function definition for ExportIR when ENABLE_DUMP_IR is not defined void ExportIR(const std::string &, const FuncGraphPtr &) { + // Static variable to track if the warning has already been printed. static bool already_printed = false; + + // Check if the warning has already been printed. if (already_printed) { return; } + + // Set already_printed to true to avoid multiple warnings. already_printed = true; + + // Output a warning indicating that the functionality of dumping function graph IR is disabled. MS_LOG(WARNING) << "The functionality of dumping function graph IR is disabled, " << "please recompile source to enable it. See help of building script."; } -#endif +#endif // ENABLE_DUMP_IR } // namespace mindspore + diff --git a/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.h b/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.h index f8dc4158086..89b08d828e8 100644 --- a/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.h +++ b/mindspore/ccsrc/pipeline/jit/debug/anf_ir_utils.h @@ -39,6 +39,7 @@ namespace mindspore { +// Helper struct and function objects for handling parameter pointers struct ParamPtrEqual { bool operator()(AnfNodePtr const &t1, AnfNodePtr const &t2) const { const ParameterPtr param1 = dyn_cast(t1); @@ -53,6 +54,7 @@ struct ParamPtrEqual { }; struct ParamPtrHasher { + // Compute a hash for a parameter pointer std::size_t operator()(AnfNodePtr const ¶m) const { const ParameterPtr parameter = dyn_cast(param); if (parameter == nullptr) { @@ -67,6 +69,7 @@ using ParamIndexMap = OrderedMapBuildShape()->cast(); TypePtr type = abs->BuildType(); + + // Create an output string stream to construct the result. std::ostringstream oss; + + // Check if both shape and type information are available. if ((shape != nullptr) && (type != nullptr)) { oss << type->DumpText() << shape->DumpText(); - } else if (type != nullptr) { + } + // Check if only type information is available. + else if (type != nullptr) { oss << type->DumpText(); - } else { + } + // If neither shape nor type information is available, label it as "Undefined." + else { oss << "Undefined"; } + + // Return the constructed string representation. return oss.str(); } + +// Helper function to get a string representation of a function graph and its arguments std::string GetGraphParamString(const FuncGraphPtr &graph, const abstract::AbstractBasePtrList &args_spec_list) { + // Check if the function graph pointer is null. MS_EXCEPTION_IF_NULL(graph); + + // Create an output string stream to construct the result. std::ostringstream oss; + + // Append the function graph's name and indicate it is a graph. oss << "graph:" << graph->ToString() << " with args["; + + // Get the list of parameters from the function graph. auto params = graph->parameters(); + + // Check if the number of parameters is less than the size of args_spec_list. if (params.size() < args_spec_list.size()) { - MS_EXCEPTION(TypeError) << "The size of parameters less than args_spec_list's size."; + MS_EXCEPTION(TypeError) << "The size of parameters is less than args_spec_list's size."; } + + // Iterate over the args_spec_list and parameters, and construct a string representation. for (size_t i = 0; i < args_spec_list.size(); i++) { auto parameter = params[i]; MS_EXCEPTION_IF_NULL(parameter); + + // Append the parameter's name and its corresponding abstract type. oss << parameter->ToString() << ":<" << GetAbstractStr(args_spec_list[i]) << ">,"; + } + + // Close the argument list and append debug information. oss << "]"; oss << GetDebugInfo(graph->debug_info(), kSourceLineTipDiscard); + + // Return the constructed string representation. return oss.str(); } + +// Dump the inference stack void DumpInferStack(std::ostringstream &oss) { + // Get the current graph evaluation stack. auto &graph_stack = GetCurrentGraphEvalStack(); + + // Check if the stack is empty. if (graph_stack.empty()) { return; } + + // Create a vector to store pairs of AnalysisContextPtr and AnfNodeConfigPtr. std::vector> infer_vec; + + // Transfer the contents of the graph stack to infer_vec and reverse the order. while (!graph_stack.empty()) { auto top = graph_stack.back(); infer_vec.push_back(top); graph_stack.pop_back(); } std::reverse(infer_vec.begin(), infer_vec.end()); + + // Initialize an index for labeling. int index = 0; + + // Iterate through the inference stack and extract information. for (const auto &item : infer_vec) { auto context = item.first; + + // Check if the context is null. if (context == nullptr) { MS_LOG(EXCEPTION) << "DumpInferStack failed, got null graph context"; } - auto graph = context->func_graph(); - if (graph == nullptr) { // Top context. + + auto graph = context->func_graph; + + // Skip the top context, which may not have a valid graph. + if (graph == nullptr) { continue; } - auto args_spec_list = context->args_spec_list(); + + auto args_spec_list = context->args_spec_list; + + // Check if the number of parameters in the graph is less than the size of args_spec_list. if (graph->parameters().size() < args_spec_list.size()) { continue; } + + // Append the graph information to the output stream. oss << " #" << index++ << " " << GetGraphParamString(graph, args_spec_list) << "\n"; } } + +// Trace the evaluation of a graph void TraceGraphEval() { + // Get the current graph evaluation stack. auto &graph_stack = GetCurrentGraphEvalStack(); + + // Check if the stack is empty. if (graph_stack.empty()) { MS_LOG(INFO) << "Length of analysis graph stack is empty."; return; } + + // Create a string stream to store the trace output. std::ostringstream oss; + + // Add a header to the trace output. oss << "\n*******************************graph evaluate stack**********************************"; oss << std::endl; + + // Dump the information from the inference stack into the output stream. DumpInferStack(oss); + + // Add a footer to the trace output. oss << "\n*************************************************************************************"; + + // Log the trace output as INFO. MS_LOG(INFO) << oss.str(); } + +// Class for exporting analyzed function graphs class AnalyzeFailExporter : public AnfExporter { public: - AnalyzeFailExporter() : AnfExporter(true, false) {} - ~AnalyzeFailExporter() override = default; + AnalyzeFailExporter() : AnfExporter(true, false) {} // Constructor + ~AnalyzeFailExporter() override = default; // Destructor + // Export a function graph given a filename and a TraceCNodeEvalStack. bool ExportFuncGraph(const std::string &filename, const TraceCNodeEvalStack &node_config_stack); protected: + // Override function to output a CNode and its inputs. void OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph, int *idx, std::map *const apply_map) override; + + // Override function to get the type of an AnfNode. std::string GetNodeType(const AnfNodePtr &nd) override; + + // Get the abstract type of an AnfNode. AbstractBasePtr GetNodeAbstract(const AnfNodePtr &nd); + + // Get the forward configuration for an AnfNode. AnfNodeConfigPtr GetForwardConfig(const AnfNodeConfigPtr &cfg); + + // Process a function graph call and update the operation comment. void ProcessFuncGraphCall(const CNodePtr &node, std::string *const op_comment); + + // Create a tagged node map for function graphs. mindspore::HashMap CreateTaggedNodeMap( const std::vector &node_config_stack); private: - AnalysisContextPtr current_context_ = nullptr; - AnalysisEnginePtr engine_ = nullptr; + AnalysisContextPtr current_context_ = nullptr; // Current analysis context + AnalysisEnginePtr engine_ = nullptr; // Analysis engine }; + +// Helper function to create a mapping of function graphs to tagged nodes mindspore::HashMap AnalyzeFailExporter::CreateTaggedNodeMap( const std::vector &node_config_stack) { - mindspore::HashSet forwarded_configs; // Check if config. is forwarded. + mindspore::HashSet forwarded_configs; // Check if config is forwarded. mindspore::HashMap tagged_func_graphs; size_t index = 0; for (auto &node_config : node_config_stack) { @@ -188,6 +277,7 @@ bool OutputAnalyzedGraphWithType(const string &file_path) { std::string AnalyzeFailExporter::GetNodeType(const AnfNodePtr &node) { if (current_context_ == nullptr) { + // If the current context is null, use the base class AnfExporter's GetNodeType. return AnfExporter::GetNodeType(node); } @@ -195,48 +285,72 @@ std::string AnalyzeFailExporter::GetNodeType(const AnfNodePtr &node) { try { FuncGraphPtr dummy_call_func_graph = nullptr; auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph); + + // Attempt to retrieve the analysis result from the cache. auto res = abstract::AnalysisResultCacheMgr::GetInstance().GetValue(cfg); + if (res != nullptr) { + // If the analysis result is available, return the type information. return GetAbstractStr(res->abstract()); } } catch (const std::exception &e) { MS_LOG(INFO) << "Exception: " << e.what(); } + + // Return "Undefined" if the analysis result is not available or if an exception occurs. return "Undefined"; } + AbstractBasePtr AnalyzeFailExporter::GetNodeAbstract(const AnfNodePtr &node) { if (current_context_ == nullptr) { + // If the current context is null, return nullptr (no abstract information available). return nullptr; } + MS_EXCEPTION_IF_NULL(engine_); try { FuncGraphPtr dummy_call_func_graph = nullptr; auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph); + + // Attempt to retrieve the analysis result from the cache. auto res = abstract::AnalysisResultCacheMgr::GetInstance().GetValue(cfg); - return res == nullptr ? nullptr : res->abstract(); + + // If the result is found, return its abstract; otherwise, return nullptr. + return (res == nullptr) ? nullptr : res->abstract(); } catch (const std::exception &e) { MS_LOG(INFO) << "Exception: " << e.what(); } + + // Return nullptr if the analysis result is not available or if an exception occurs. return nullptr; } + AnfNodeConfigPtr AnalyzeFailExporter::GetForwardConfig(const AnfNodeConfigPtr &cfg) { MS_EXCEPTION_IF_NULL(cfg); MS_EXCEPTION_IF_NULL(engine_); AnfNodeConfigPtr cur_cfg = cfg; + + // Look for the forward configuration in the engine's map. auto iter = engine_->anfnode_config_map().find(cur_cfg); while (iter != engine_->anfnode_config_map().end()) { auto node = cur_cfg->node(); cur_cfg = iter->second; MS_LOG(DEBUG) << "Get forward node: " << node << "[" << node->DebugString() << "] --> " << cur_cfg->node() << "[" << cur_cfg->node()->DebugString() << "]"; + + // Continue searching for the next forward configuration. iter = engine_->anfnode_config_map().find(cur_cfg); } + + // Return the ultimate configuration that is not forwarded. return cur_cfg; } + void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string *const op_comment) { + // Check if the input node is nullptr if (node == nullptr) { MS_LOG(ERROR) << "Node is nullptr"; return; @@ -244,30 +358,41 @@ void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string CNodePtr cnode = nullptr; try { FuncGraphPtr dummy_call_func_graph = nullptr; + // Create a configuration for the node using the engine auto cfg = engine_->MakeConfig(node, current_context_, dummy_call_func_graph); + // Get the forward configuration cfg = GetForwardConfig(cfg); MS_EXCEPTION_IF_NULL(cfg); + // Attempt to cast the configuration node to a CNode cnode = dyn_cast(cfg->node()); } catch (const std::exception &e) { + // Handle exceptions by logging the error message MS_LOG(INFO) << "Exception: " << e.what(); } + // Check if the CNode is nullptr if (cnode == nullptr) { MS_LOG(INFO) << "CNode is nullptr"; return; } + // Get the inputs of the CNode const auto &inputs = cnode->inputs(); for (size_t i = 0; i < inputs.size(); ++i) { + // Get the abstract value of the input auto op_abs = GetNodeAbstract(inputs[i]); if (op_abs == nullptr) { - MS_LOG(DEBUG) << "Abstract of inputs[" << i << "] of cnode " << cnode->ToString() << " is nullptr"; + // Log a message if the abstract value is nullptr + MS_LOG(DEBUG) << "Abstract of inputs[" << i << "] of cnode " << cnode->ToString() << " is nullptr"; continue; } + // Check if the abstract value is not a function if (!op_abs->isa() && !op_abs->isa()) { + // Log a message indicating that the input is not a function MS_LOG(DEBUG) << "Inputs[" << i << "] of cnode " << cnode->ToString() << " is of type " << op_abs->type_name() - << ", not function, ignore it"; - // Get prototype of VirtualEvaluator for printing + << ", not a function, ignore it"; + + // Get the prototype of VirtualEvaluator for printing if (i == 0 && op_abs->isa()) { auto func = dyn_cast(op_abs); std::ostringstream oss; @@ -282,50 +407,74 @@ void AnalyzeFailExporter::ProcessFuncGraphCall(const CNodePtr &node, std::string oss << GetAbstractStr(arg); } oss << ") -> " << GetAbstractStr(func->output()) << " "; + // Store the prototype string in op_comment *op_comment = oss.str(); } } } } + void AnalyzeFailExporter::OutputCNode(std::ostringstream &oss, const CNodePtr &cnode, const FuncGraphPtr &func_graph, int *idx, std::map *const apply_map) { + // Output the text representation of the CNode to the provided output stream OutputCNodeText(oss, cnode, func_graph, idx, apply_map); - // Process function graph call + + // Process the function graph call and generate a prototype comment std::string op_comment; ProcessFuncGraphCall(cnode, &op_comment); + + // Check if op_comment is not empty (i.e., a prototype comment was generated) if (!op_comment.empty()) { auto &inputs = cnode->inputs(); + // Output a comment with the prototype information oss << " #" << GetAnfNodeText(func_graph, inputs[0], *apply_map) << ".prototype = " << op_comment; } - // Output comment + + // Output any additional comments associated with the CNode OutputStatementComment(oss, cnode); + + // Add a newline character to separate this CNode from the next one oss << "\n"; } + +// Export analyzed function graphs to a file bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const TraceCNodeEvalStack &node_config_stack) { + // Check if the node_config_stack is empty if (node_config_stack.empty()) { MS_LOG(DEBUG) << "Node configs is empty"; return false; } + + // Create the real filepath for the export file auto real_filepath = Common::CreatePrefixPath(filename); if (!real_filepath.has_value()) { - MS_LOG(ERROR) << "The export ir path: " << filename << " is not illegal."; + MS_LOG(ERROR) << "The export ir path: " << filename << " is not legal."; return false; } + + // Change the file mode to make it writable ChangeFileMode(real_filepath.value(), S_IWUSR); + + // Open the export file for writing std::ofstream ofs(real_filepath.value()); if (!ofs.is_open()) { MS_LOG(ERROR) << "Open file '" << real_filepath.value() << "' failed!" << ErrnoToString(errno); return false; } + // Set the engine if it is not already set if (engine_ == nullptr) { engine_ = node_config_stack.front()->engine(); } + // Create a map of tagged function graphs based on node_config_stack auto tagged_func_graphs = CreateTaggedNodeMap(node_config_stack); - mindspore::HashSet printed_func_graphs; // Check if func graph has been printed. + + // Create a HashSet to keep track of printed function graphs + mindspore::HashSet printed_func_graphs; + // Output graph on the analysis stack for (const auto &node_config : node_config_stack) { MS_EXCEPTION_IF_NULL(node_config); @@ -334,21 +483,33 @@ bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const Tra << ", FV: " << (node_config->func_graph() != node_config->context()->func_graph()) << ", calling func graph: " << node_config->func_graph()->ToString() << ", context func graph: " << node_config->context()->func_graph()->ToString(); + + // Check if the function graph is null if (fg == nullptr) { MS_LOG(ERROR) << "FuncGraph is null, context: " << node_config->ToString(); continue; } + + // Check if the function graph has already been printed if (printed_func_graphs.find(fg) != printed_func_graphs.end()) { continue; } + + // Add the function graph to the printed set (void)printed_func_graphs.emplace(fg); - current_context_ = node_config->context(); // Set current context. + // Set the current context + current_context_ = node_config->context(); + + // Create a buffer to store the export content for the function graph std::ostringstream buffer; ExportOneFuncGraph(buffer, fg, tagged_func_graphs[fg]); + + // Write the content to the export file ofs << buffer.str() << "\n\n"; } + // Output a separator and the number of function graphs processed ofs << "#===============================================================================\n"; ofs << "# num of function graphs in stack: "; auto ignored_num = (node_config_stack.size() - printed_func_graphs.size()); @@ -358,70 +519,115 @@ bool AnalyzeFailExporter::ExportFuncGraph(const std::string &filename, const Tra ofs << printed_func_graphs.size() << "/" << node_config_stack.size() << " (Ignored " << ignored_num << " internal frames).\n"; } + + // Close the export file ofs.close(); + + // Change the file mode to make it readable ChangeFileMode(real_filepath.value(), S_IRUSR); + return true; } + +// Get the path for the "analyze_fail.dat" file std::string GetEvalFailDatPath() { std::string path; + + // Try to get the "MS_OM_PATH" environment variable auto ms_om_path = common::GetEnv("MS_OM_PATH"); + + // Check if the environment variable is set if (!ms_om_path.empty()) { + // If set, use its value as the path path = ms_om_path; } else { + // If not set, use the current directory as the path path = "."; } + + // Append the rank and the "/om/analyze_fail.dat" to the path path += "/rank_" + std::to_string(GetRank()) + "/om/analyze_fail.dat"; - // Support "../" in path. + + // Support "../" in the path by creating a real path auto realpath = Common::CreatePrefixPath(path, true); + + // Check if the real path was successfully created if (!realpath.has_value()) { + // If not, raise an exception with an error message MS_EXCEPTION(ValueError) << "Get real path failed. path=" << path; } + + // Return the real path as the result return realpath.value(); } + +// Get evaluation stack information and write it to the provided output stream void GetEvalStackInfo(std::ostringstream &oss) { MS_LOG(INFO) << "Get graph analysis information begin"; + + // Get the CNode debug stack auto stack = GetCNodeDebugStack(); + + // Check if the stack is empty if (stack.empty()) { MS_LOG(INFO) << "Length of analysis information stack is empty."; return; } + + // Write a header to the output stream oss << "\nThe function call stack"; + + // If security is not enabled, obtain the file name for "analyze_fail.dat" using GetEvalFailDatPath() #ifndef ENABLE_SECURITY std::string file_name = GetEvalFailDatPath(); auto ret = OutputAnalyzedGraphWithType(file_name); + + // If writing to the file is successful, add a message to the output stream with the file name if (ret) { oss << " (See file '" << file_name << "' for more details)"; } #endif + + // Add a colon to the output stream to separate the header from the stack information oss << ":\n"; int index = 0; std::string last_location_info = ""; + + // Iterate over the stack elements for (size_t i = 0; i < stack.size(); ++i) { auto node_config = stack[i]; MS_EXCEPTION_IF_NULL(node_config); auto cnode = dyn_cast(node_config->node()); + + // Check if the CNode is nullptr if (cnode == nullptr) { MS_LOG(DEBUG) << "CNode of elements[" << i << "] is nullptr."; continue; } + // Get the debug information for the CNode auto debug_info = cnode->debug_info(); auto this_location_info = trace::GetDebugInfo(debug_info, std::string("")); + + // Check if the debug information is empty or identical to the previous one if (this_location_info.empty() || this_location_info == last_location_info) { continue; } + // Update the last location information and add it to the output stream last_location_info = this_location_info; oss << "# " << index++ << " " << this_location_info; } + // Clear the stack and log the end of graph analysis information stack.clear(); MS_LOG(INFO) << "Get graph analysis information *end*"; } + // Trace the graph evaluator stack thread_local TraceGraphEvalStack graph_infer_stack; // Trace the cnode infer debug info @@ -468,23 +674,40 @@ void ClearTraceStack() { cnode_debug_stack.clear(); } +// Get trace stack information and write it to the provided output stream void GetTraceStackInfo(std::ostringstream &oss) { + // Trace the current graph evaluation TraceGraphEval(); + + // Create an empty ostringstream to store evaluation stack information std::ostringstream trace_info; + + // Get evaluation stack information and store it in trace_info GetEvalStackInfo(trace_info); + + // Check if trace_info is empty if (trace_info.str().empty()) { + // If it's empty, attempt to retrieve debug information using TraceManager DebugInfoPtr debug_info = TraceManager::record_debug_info(); + + // Check if debug_info is not nullptr and the record_debug_info_flag is true if (debug_info != nullptr && TraceManager::record_debug_info_flag() == true) { + // Get the debug information as a string auto debug_str = trace::GetDebugInfo(debug_info); + + // Check if the debug_str is not empty if (!debug_str.empty()) { + // Add the debug_str to the output stream oss << "\n\n# " << debug_str; } } } else { + // If trace_info is not empty, add it to the output stream oss << trace_info.str(); } } + // Register trace provider to LogWriter. struct TraceProviderRegister { TraceProviderRegister() { LogWriter::set_trace_provider(GetTraceStackInfo); } diff --git a/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc b/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc index 6da9e08559c..0121e48b8db 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc @@ -37,10 +37,19 @@ namespace mindspore { namespace parse { namespace { + +// This structure is used to register a callback function for converting Python data to MindSpore values. struct PyDataToValueRegister { - PyDataToValueRegister() { python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue); } + // Constructor of the structure. + PyDataToValueRegister() { + // Register the PyDataToValue handler from the Python adapter module. + python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue); + } } callback_register; + } // namespace + +// Define common data types and aliases for better readability. using Tensor = mindspore::tensor::Tensor; using TensorPtr = mindspore::tensor::TensorPtr; using MetaTensor = mindspore::tensor::MetaTensor; @@ -50,21 +59,28 @@ using CSRTensorPtr = mindspore::tensor::CSRTensorPtr; using COOTensor = mindspore::tensor::COOTensor; using COOTensorPtr = mindspore::tensor::COOTensorPtr; +// Define function types for instance checking and conversion. using InstanceCheckFunc = std::function; using InstanceConvertFunc = std::function; + +// Constants for bit sizes. static constexpr int kBit8 = 8; static constexpr int kBit16 = 16; static constexpr int kBit32 = 32; static constexpr int kBit64 = 64; +// Class for data conversion. class DataConverter { public: + // Constructor for DataConverter, taking an instance conversion function. explicit DataConverter(InstanceConvertFunc convert_func) : convert_func_(std::move(convert_func)) {} virtual ~DataConverter() = default; + // Check if the given Python object matches the conversion type. virtual bool Matched(const py::object &obj) = 0; + // Convert a Python object to a MindSpore value. virtual ValuePtr ConvertPyObject(const py::object &obj, bool use_sig, const TypePtr &dtype) { if (convert_func_ == nullptr) { MS_LOG(EXCEPTION) << "convert func is null"; @@ -82,29 +98,34 @@ using ArgsObjConvertFunc = std::function; using ArgsObjSigConvertFunc = std::function; using ArgsOjbTypeConvertFunc = std::function; -// Convert the data according instance type +// Template class for data conversion based on instance type. template class ByTypeDataConverter : public DataConverter { public: + // Constructor for ByTypeDataConverter, taking an instance conversion function. explicit ByTypeDataConverter(const InstanceConvertFunc &convert_func) : DataConverter(convert_func), check_func_(py::isinstance) {} + // Constructor for ByTypeDataConverter, taking a converted type. explicit ByTypeDataConverter(const ValuePtr &converted_type) : DataConverter( [converted_type](const py::object &, bool, const TypePtr &) -> ValuePtr { return converted_type; }), check_func_(py::isinstance) {} + // Constructor for ByTypeDataConverter, taking an argument object conversion function. explicit ByTypeDataConverter(const ArgsObjConvertFunc &convert_func) : DataConverter( [convert_func](const py::object &obj, bool, const TypePtr &) -> ValuePtr { return convert_func(obj); }), check_func_(py::isinstance) {} + // Constructor for ByTypeDataConverter, taking an argument object with signature conversion function. explicit ByTypeDataConverter(const ArgsObjSigConvertFunc &convert_func) : DataConverter([convert_func](const py::object &obj, bool use_sig, const TypePtr &) -> ValuePtr { return convert_func(obj, use_sig); }), check_func_(py::isinstance) {} + // Constructor for ByTypeDataConverter, taking an argument object with type conversion function. explicit ByTypeDataConverter(const ArgsOjbTypeConvertFunc &convert_func) : DataConverter([convert_func](const py::object &obj, bool, const TypePtr &dtype) -> ValuePtr { return convert_func(obj, dtype); @@ -113,13 +134,14 @@ class ByTypeDataConverter : public DataConverter { ~ByTypeDataConverter() override = default; + // Check if the given Python object matches the instance type. bool Matched(const py::object &obj) override { return check_func_ != nullptr ? check_func_(obj) : false; } private: InstanceCheckFunc check_func_ = nullptr; }; -// Convert the data according object attribute. +// Data converter class for converting Python objects based on object attributes. class ByAttrDataConverter : public DataConverter { public: ByAttrDataConverter(const std::string &attr_name, const ArgsObjConvertFunc &convert_func) @@ -135,28 +157,35 @@ class ByAttrDataConverter : public DataConverter { ~ByAttrDataConverter() override = default; + // Check if the given Python object has the specified attribute. bool Matched(const py::object &obj) override { return py::hasattr(obj, attr_name_.c_str()); } private: std::string attr_name_; }; +// Convert the given Python object to a FuncGraphPtr. FuncGraphPtr ConvertToBpropCut(const py::object &obj) { + // Extract the object key. std::vector results = data_converter::GetObjKey(obj); std::string obj_key = results[0]; + + // Get the custom bprop function. py::function bprop_func = py::getattr(obj, CUSTOM_BPROP_NAME); + // Create a new FuncGraph. auto bprop_graph = std::make_shared(); std::vector outputs; + // Create a fake bprop Primitive with a backward hook. auto fake_bprop = std::make_shared("bprop_cut"); fake_bprop->AddBackwardHookFn(0, bprop_func); (void)fake_bprop->AddAttr(CUSTOM_BPROP_NAME, MakeValue(true)); outputs.push_back(NewValueNode(fake_bprop)); + // Extract parameters from the bprop function. py::object code_obj = py::getattr(bprop_func, "__code__"); - // Three parameters self, out and dout need to be excluded - constexpr auto kBpropExcludeParamNum = 3; + constexpr auto kBpropExcludeParamNum = 3; // Exclude self, out, and dout parameters size_t inputs_num = py::cast(py::getattr(code_obj, "co_argcount")) - kBpropExcludeParamNum; for (size_t i = 0; i < inputs_num; ++i) { auto param = bprop_graph->add_parameter(); @@ -167,12 +196,17 @@ FuncGraphPtr ConvertToBpropCut(const py::object &obj) { outputs.push_back(p1); outputs.push_back(p2); + // Set the output of the FuncGraph. bprop_graph->set_output(bprop_graph->NewCNode(std::move(outputs))); + + // Store the FuncGraph in the object graph value. data_converter::SetObjGraphValue(obj_key, bprop_graph); + return bprop_graph; } namespace { +// Convert a Python tuple object to a ValuePtr. ValuePtr ConvertTuple(const py::object &obj, bool use_signature) { MS_LOG(DEBUG) << "Converting python tuple"; auto tuple = obj.cast(); @@ -188,6 +222,7 @@ ValuePtr ConvertTuple(const py::object &obj, bool use_signature) { return std::make_shared(value_list); } +// Convert a Python list object to a ValuePtr. ValuePtr ConvertList(const py::object &obj, bool use_signature) { MS_LOG(DEBUG) << "Converting python list"; @@ -204,6 +239,7 @@ ValuePtr ConvertList(const py::object &obj, bool use_signature) { return std::make_shared(value_list); } +// Convert a Python cell list object to a ValuePtr. ValuePtr ConvertCellList(const py::object &obj, bool use_signature) { MS_LOG(DEBUG) << "Converting cell list"; py::sequence list = obj; @@ -219,6 +255,7 @@ ValuePtr ConvertCellList(const py::object &obj, bool use_signature) { return std::make_shared(value_list); } +// Convert a Python dict object to a ValuePtr. ValuePtr ConvertDict(const py::object &obj, bool use_signature) { MS_LOG(DEBUG) << "Converting python dict"; @@ -240,6 +277,7 @@ ValuePtr ConvertDict(const py::object &obj, bool use_signature) { return std::make_shared(key_values); } +// Convert a Python module namespace object to a ValuePtr. ValuePtr ConvertModuleNameSpace(const py::object &obj) { MS_LOG(DEBUG) << "Converting python module"; py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); @@ -250,6 +288,7 @@ ValuePtr ConvertModuleNameSpace(const py::object &obj) { return converted; } +// Convert a Python data class object to a ValuePtr. ValuePtr ConvertDataClass(const py::object &obj) { MS_LOG(DEBUG) << "Converting dataclass"; // Maybe the obj is dataclass define @@ -258,7 +297,13 @@ ValuePtr ConvertDataClass(const py::object &obj) { auto converted = std::make_shared(obj, std::string(desc.begin() + 1, desc.end() - 1)); return converted; } - +// ConvertMsClass function +// This function converts a class instance decorated with ms_class to a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the class instance. +// Parameters: +// - obj: The Python object to be converted. +// Returns: +// - A ValuePtr representing the class instance. ValuePtr ConvertMsClass(const py::object &obj) { MS_LOG(DEBUG) << "Converting ms class"; // Convert class instance decorated with ms_class. @@ -268,14 +313,22 @@ ValuePtr ConvertMsClass(const py::object &obj) { return std::make_shared(obj, cls_name); } +// ConvertPrimitive function +// This function converts a primitive object to a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the primitive. +// Parameters: +// - obj: The Python object to be converted. +// - use_signature: A flag indicating whether to use signature for the primitive (default is false). +// Returns: +// - A ValuePtr representing the primitive. ValuePtr ConvertPrimitive(const py::object &obj, bool use_signature = false) { MS_LOG(DEBUG) << "Converting primitive object" << use_signature; - // need check the primitive is class type or instance + // Check if the primitive is a class type or instance auto obj_type = data_converter::GetObjType(obj); if (obj_type == RESOLVE_TYPE_CLASS_TYPE) { auto desc = py::cast(python_adapter::CallPyObjMethod(obj, PYTHON_GET_OBJ_DESC, obj)); - // desc has format "", strip the '<' and '>' by offset 1. + // 'desc' has format "", strip the '<' and '>' by offset 1. return std::make_shared(obj, std::string(desc.begin() + 1, desc.end() - 1)); } py::object adapter_obj = obj; @@ -299,6 +352,14 @@ ValuePtr ConvertPrimitive(const py::object &obj, bool use_signature = false) { return primitive; } +// ConvertMetaFuncGraph function +// This function converts a MetaFuncGraph object to a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the MetaFuncGraph. +// Parameters: +// - obj: The Python object to be converted. +// - use_signature: A flag indicating whether to use signature for the MetaFuncGraph (default is false). +// Returns: +// - A ValuePtr representing the MetaFuncGraph. ValuePtr ConvertMetaFuncGraph(const py::object &obj, bool use_signature = false) { MS_LOG(DEBUG) << "Converting MetaFuncGraph object"; auto meta = obj.cast(); @@ -312,6 +373,13 @@ ValuePtr ConvertMetaFuncGraph(const py::object &obj, bool use_signature = false) return meta; } +// ConvertFuncGraph function +// This function converts a FuncGraph object to a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the FuncGraph. +// Parameters: +// - obj: The Python object to be converted. +// Returns: +// - A ValuePtr representing the FuncGraph. ValuePtr ConvertFuncGraph(const py::object &obj) { MS_LOG(DEBUG) << "Converting FuncGraph object"; auto func_graph = obj.cast(); @@ -323,6 +391,13 @@ ValuePtr ConvertFuncGraph(const py::object &obj) { return func_graph; } +// ConvertSlice function +// This function converts a Python slice object to a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the slice. +// Parameters: +// - obj: The Python slice object to be converted. +// Returns: +// - A ValuePtr representing the slice. ValuePtr ConvertSlice(const py::object &obj) { MS_LOG(DEBUG) << "Converting slice object"; @@ -347,13 +422,20 @@ ValuePtr ConvertSlice(const py::object &obj) { return std::make_shared(start, stop, step); } +// ConvertCellObjToFuncGraph function +// This function converts a Cell object to a FuncGraph and returns it as a ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the FuncGraph. +// Parameters: +// - obj: The Python Cell object to be converted. +// Returns: +// - A ValuePtr representing the FuncGraph. ValuePtr ConvertCellObjToFuncGraph(const py::object &obj) { FuncGraphPtr func_graph = ConvertToFuncGraph(obj); if (func_graph == nullptr) { MS_LOG(ERROR) << "Parse resolve function error."; return nullptr; } - // if the cell object has specified bprop, it has user-defined bprop function parse and record it + // If the cell object has specified bprop, it has a user-defined bprop function parsed and recorded. if (py::hasattr(obj, CUSTOM_BPROP_NAME)) { bool enable_bprop_debug = py::cast(py::getattr(obj, "bprop_debug")); FuncGraphPtr bprop_graph = @@ -371,13 +453,20 @@ ValuePtr ConvertCellObjToFuncGraph(const py::object &obj) { return func_graph; } +// ConvertOtherObj function +// This function converts other Python objects to ValuePtr. +// It takes a Python object 'obj' as input and returns a ValuePtr representing the object. +// Parameters: +// - obj: The Python object to be converted. +// Returns: +// - A ValuePtr representing the object. ValuePtr ConvertOtherObj(const py::object &obj) { auto obj_type = data_converter::GetObjType(obj); MS_LOG(DEBUG) << "Converting the object(" << ((std::string)py::str(obj)) << ") detail type: " << obj_type << " "; if (obj_type == RESOLVE_TYPE_CLASS_TYPE) { - MS_LOG(DEBUG) << "Resolve the class type, need create class instance."; + MS_LOG(DEBUG) << "Resolve the class type, need to create a class instance."; std::string desc = py::str(obj); - // desc has format "", strip the '<' and '>' by offset 1. + // 'desc' has format "", strip the '<' and '>' by offset 1. return std::make_shared(obj, std::string(desc.begin() + 1, desc.end() - 1)); } if (obj_type == RESOLVE_TYPE_FUNCTION || obj_type == RESOLVE_TYPE_METHOD) { @@ -390,8 +479,8 @@ ValuePtr ConvertOtherObj(const py::object &obj) { return func_graph; } if (obj_type == RESOLVE_TYPE_CLASS_INSTANCE) { - // Create the namespace for common class instance - // When the obj is Cell, default parse the 'construct' + // Create the namespace for common class instance. + // When the obj is Cell, default parse the 'construct'. py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); py::object namespace_var = python_adapter::CallPyModFn(mod, PYTHON_MOD_GET_MEMBER_NAMESPACE_SYMBOL, obj); auto res = std::make_shared(RESOLVE_NAMESPACE_NAME_CLASS_MEMBER, namespace_var); @@ -399,8 +488,8 @@ ValuePtr ConvertOtherObj(const py::object &obj) { return res; } // Start RESOLVE_TYPE_INVALID... - // The fallback feature is enabled in default. - // Not support change the flag during the process is alive. + // The fallback feature is enabled by default. + // Not supporting changing the flag while the process is alive. static const auto support_fallback = common::GetEnv("MS_DEV_ENABLE_FALLBACK"); static const auto use_fallback = (support_fallback != "0"); if (use_fallback) { @@ -411,11 +500,23 @@ ValuePtr ConvertOtherObj(const py::object &obj) { MS_LOG(ERROR) << "Resolve type is invalid, obj: " << py::str(obj); return nullptr; } - +/** + * @brief Converts a number-like object of type T to a MindSpore Value with the specified dtype. + * + * This function takes an object of type T and a MindSpore TypePtr dtype as input and returns a MindSpore ValuePtr. + * It converts the object to the specified data type and wraps it as a ValuePtr. + * + * @tparam T The type of the input object (e.g., int64_t, float). + * @param obj The input object to be converted. + * @param dtype The target data type to convert the object to. + * @return A ValuePtr containing the converted value. + */ template ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) { ValuePtr data = nullptr; auto int_dypte = dyn_cast(dtype); + + // Check if dtype is an integer type if (int_dypte != nullptr) { switch (int_dypte->nbits()) { case kBit8: @@ -437,6 +538,8 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) { } auto uint_dypte = dyn_cast(dtype); + + // Check if dtype is an unsigned integer type if (uint_dypte != nullptr) { switch (uint_dypte->nbits()) { case kBit8: @@ -458,6 +561,8 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) { } auto float_dypte = dyn_cast(dtype); + + // Check if dtype is a floating-point type if (float_dypte != nullptr) { switch (float_dypte->nbits()) { case kBit32: @@ -474,6 +579,17 @@ ValuePtr ConvertNumberWithType(const T &obj, const TypePtr &dtype) { return nullptr; } +/** + * @brief Converts a Python integer object to a MindSpore Value with the specified dtype. + * + * This function takes a Python integer object and an optional MindSpore TypePtr dtype as input, + * and returns a MindSpore ValuePtr containing the converted value with the specified dtype. + * If dtype is not provided, it defaults to Int64. + * + * @param obj The Python integer object to be converted. + * @param dtype The target data type to convert the integer to (optional). + * @return A ValuePtr containing the converted integer value. + */ ValuePtr ConvertIntegerWithType(const py::object &obj, const TypePtr &dtype = nullptr) { auto obj_int64 = py::cast(obj); if (dtype == nullptr) { @@ -482,6 +598,17 @@ ValuePtr ConvertIntegerWithType(const py::object &obj, const TypePtr &dtype = nu return ConvertNumberWithType(obj_int64, dtype); } +/** + * @brief Converts a Python floating-point number object to a MindSpore Value with the specified dtype. + * + * This function takes a Python floating-point number object and an optional MindSpore TypePtr dtype as input, + * and returns a MindSpore ValuePtr containing the converted value with the specified dtype. + * If dtype is not provided, it defaults to FP32. + * + * @param obj The Python floating-point number object to be converted. + * @param dtype The target data type to convert the floating-point number to (optional). + * @return A ValuePtr containing the converted floating-point value. + */ ValuePtr ConvertFloatWithType(const py::object &obj, const TypePtr &dtype = nullptr) { auto obj_float64 = py::cast(obj); if (dtype == nullptr) { @@ -490,16 +617,41 @@ ValuePtr ConvertFloatWithType(const py::object &obj, const TypePtr &dtype = null return ConvertNumberWithType(obj_float64, dtype); } +/** + * @brief Converts a Python object to a MindSpore Value of type T. + * + * This template function takes a Python object and returns a MindSpore ValuePtr containing the converted value of type T. + * + * @tparam T The target type for conversion (e.g., Tensor, MetaTensor, Variable). + * @param obj The Python object to be converted. + * @return A ValuePtr containing the converted value of type T. + */ template ValuePtr PyCast(const py::object &obj) { return std::make_shared(py::cast(obj)); } +/** + * @brief Converts a Python object to a MindSpore Value of type T. + * + * This template function takes a Python object and returns a MindSpore ValuePtr containing the converted value of type T. + * + * @tparam T The target type for conversion (e.g., Tensor, MetaTensor, Variable). + * @param obj The Python object to be converted. + * @return A ValuePtr containing the converted value of type T. + */ template ValuePtr ObjCast(const py::object &obj) { return obj.cast(); } +/** + * @brief Gets a vector of DataConverterPtr objects for data conversion. + * + * This function returns a vector of DataConverterPtr objects, each responsible for converting specific Python data types to MindSpore Values. + * + * @return A vector of DataConverterPtr objects. + */ static const std::vector &GetDataConverters() { static const std::vector data_converters{ // Convert data by python object type. @@ -539,8 +691,19 @@ static const std::vector &GetDataConverters() { }; return data_converters; } -} // namespace +/** + * @brief Converts a Python object to a MindSpore Value. + * + * This function takes a Python object and returns a MindSpore ValuePtr containing the converted value. + * It iterates through a list of DataConverterPtr objects to find the appropriate converter for the given object. + * + * @param obj The Python object to be converted. + * @param data A pointer to a ValuePtr where the converted value will be stored. + * @param use_signature A boolean indicating whether to use a signature for conversion (default is false). + * @param dtype The target data type for conversion (optional). + * @return True if the conversion is successful, false otherwise. + */ bool ConvertData(const py::object &obj, ValuePtr *data, bool use_signature, const TypePtr &dtype) { // Check parameter valid if (data == nullptr) { @@ -564,7 +727,18 @@ bool ConvertData(const py::object &obj, ValuePtr *data, bool use_signature, cons return converted != nullptr; } -// Convert data to graph +/** + * @brief Converts a Python object to a MindSpore FuncGraph. + * + * This function takes a Python object and a string indicating the method for parsing Python code. + * It returns a MindSpore FuncGraphPtr that represents the converted object. + * If the object has been previously cached, it is retrieved from the cache. + * If not, the object is parsed and converted to a FuncGraph, and the converted FuncGraph is cached for future use. + * + * @param obj The Python object to be converted to a FuncGraph. + * @param python_mod_get_parse_method The method for parsing Python code (e.g., "__getitem__"). + * @return A FuncGraphPtr representing the converted object. + */ FuncGraphPtr ConvertToFuncGraph(const py::object &obj, const std::string &python_mod_get_parse_method) { std::vector results = data_converter::GetObjKey(obj); std::string obj_id = results[0] + python_mod_get_parse_method; @@ -599,24 +773,30 @@ FuncGraphPtr ConvertToFuncGraph(const py::object &obj, const std::string &python return func_graph; } - namespace data_converter { + +// A map to store objects and their corresponding values. static mindspore::HashMap object_map_; +// A map to store objects and their corresponding FuncGraphPtrs. static mindspore::HashMap> object_graphs_map_; +// Set a FuncGraphPtr for an object key. void SetObjGraphValue(const std::string &obj_key, const FuncGraphPtr &data) { object_graphs_map_[obj_key].push_back(data); MS_LOG(DEBUG) << "Set func graph size: " << object_graphs_map_.size(); } +// Get the map of object keys and their corresponding FuncGraphPtrs. const mindspore::HashMap> &GetObjGraphs() { MS_LOG(DEBUG) << "Obj graphs size: " << object_graphs_map_.size(); return object_graphs_map_; } +// Cache a ValuePtr for an object key. void CacheObjectValue(const std::string &obj_key, const ValuePtr &data) { object_map_[obj_key] = data; } +// Get the ValuePtr for an object key. bool GetObjectValue(const std::string &obj_key, ValuePtr *data) { if (object_map_.count(obj_key)) { *data = object_map_[obj_key]; @@ -625,6 +805,7 @@ bool GetObjectValue(const std::string &obj_key, ValuePtr *data) { return false; } +// Get object keys for a Python object. std::vector GetObjKey(const py::object &obj) { py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); py::tuple obj_tuple = python_adapter::CallPyModFn(mod, PYTHON_MOD_RESOLVE_GET_OBJ_KEY, obj); @@ -634,7 +815,7 @@ std::vector GetObjKey(const py::object &obj) { return {py::cast(obj_tuple[0]), py::cast(obj_tuple[1])}; } -// Get obj detail type +// Get the ResolveTypeDef (type identifier) of a Python object. ResolveTypeDef GetObjType(const py::object &obj) { try { py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); @@ -642,15 +823,15 @@ ResolveTypeDef GetObjType(const py::object &obj) { ResolveTypeDef(python_adapter::CallPyModFn(mod, PYTHON_MOD_RESOLVE_GET_OBJ_TYPE, obj).cast()); return obj_type; } catch (const py::error_already_set &ex) { - MS_LOG(ERROR) << "Meet a exception from Python when get the type of \'" << py::str(obj) << "\'.\n" << ex.what(); + MS_LOG(ERROR) << "Meet an exception from Python when getting the type of \'" << py::str(obj) << "\'.\n" << ex.what(); std::rethrow_exception(std::current_exception()); } catch (const py::type_error &ex) { - MS_LOG(ERROR) << "Meet a exception when get the type of \'" << py::str(obj) << "\'.\n" << ex.what(); + MS_LOG(ERROR) << "Meet an exception when getting the type of \'" << py::str(obj) << "\'.\n" << ex.what(); std::rethrow_exception(std::current_exception()); } } -// Get class instance detail type. +// Get the ClassInstanceTypeDef (class instance type identifier) of a Python object. ClassInstanceTypeDef GetClassInstanceType(const py::object &obj) { py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); auto class_type = @@ -658,22 +839,22 @@ ClassInstanceTypeDef GetClassInstanceType(const py::object &obj) { return class_type; } -// Check the object is Cell Instance. +// Check if the object is an instance of the Cell class. bool IsCellInstance(const py::object &obj) { auto class_type = GetClassInstanceType(obj); bool is_cell = (class_type == CLASS_INSTANCE_TYPE_CELL); return is_cell; } -// Create the python class instance. +// Create a Python class instance. py::object CreatePythonObject(const py::object &type, const py::tuple &args_kwargs) { py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); - // `args_kwargs` maybe a tuple(*args), tuple(**kwargs), or tuple(*args, **kwargs). + // `args_kwargs` may be a tuple(*args), tuple(**kwargs), or tuple(*args, **kwargs). return args_kwargs.empty() ? python_adapter::CallPyModFn(mod, PYTHON_MOD_CREATE_INSTANCE, type) : python_adapter::CallPyModFn(mod, PYTHON_MOD_CREATE_INSTANCE, type, args_kwargs); } -// Call the python script string. +// Call a Python script string. py::object CallPythonScript(const py::object &script, const py::tuple &args_kwargs) { py::module mod = python_adapter::GetPyModule(PYTHON_MOD_PARSE_MODULE); // `args_kwargs` is a tuple(dict(global), dict(local)). @@ -681,12 +862,12 @@ py::object CallPythonScript(const py::object &script, const py::tuple &args_kwar : python_adapter::CallPyModFn(mod, PYTHON_MOD_EVAL_PY_SCRIPT, script, args_kwargs); } -// Generate an appropriate name and set to graph debuginfo, -// character <> can not used in the dot file, so change to another symbol. +// Generate an appropriate name and set it to the FuncGraph's debuginfo. +// Characters '<' and '>' cannot be used in the dot file, so they are replaced with '「' and '」'. void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name) { MS_EXCEPTION_IF_NULL(func_graph); MS_EXCEPTION_IF_NULL(func_graph->debug_info()); - // Set detail name info of function + // Set detailed name info of the function std::ostringstream oss; for (size_t i = 0; i < name.size(); i++) { if (name[i] == '<') { @@ -700,6 +881,7 @@ void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name) func_graph->debug_info()->set_full_name(oss.str()); } +// Convert a Python object to a ValuePtr. ValuePtr PyDataToValue(const py::object &obj) { py::object to_convert = obj; ValuePtr value = nullptr; @@ -707,15 +889,18 @@ ValuePtr PyDataToValue(const py::object &obj) { return value; } +// Clear the object cache. void ClearObjectCache() { object_map_.clear(); object_graphs_map_.clear(); } + } // namespace data_converter +// A map to store data class names and their corresponding ClassPtrs. static mindspore::HashMap g_dataClassToClass = {}; -// Parse dataclass to mindspore Class type +// Parse a data class to a mindspore Class type. ClassPtr ParseDataClass(const py::object &cls_obj) { std::string cls_name = py::cast(python_adapter::GetPyObjAttr(cls_obj, "__name__")); std::string cls_module = py::cast(python_adapter::GetPyObjAttr(cls_obj, "__module__")); @@ -745,8 +930,7 @@ ClassPtr ParseDataClass(const py::object &cls_obj) { } std::shared_ptr me_class = std::make_shared(Named(cls_name), attributes, methods_map); - // static Variable for cache - // cppcheck-suppress unreadVariable + // Static Variable for cache g_dataClassToClass[cls] = me_class; return me_class; diff --git a/mindspore/ccsrc/pipeline/jit/parse/data_converter.h b/mindspore/ccsrc/pipeline/jit/parse/data_converter.h index 9c7828d1fb3..207c9a70246 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/data_converter.h +++ b/mindspore/ccsrc/pipeline/jit/parse/data_converter.h @@ -30,30 +30,57 @@ namespace mindspore { namespace parse { -// data convert for parse namespace data_converter { + +// Cache the ValuePtr associated with the given object key. void CacheObjectValue(const std::string &obj_key, const ValuePtr &data); + +// Get the ValuePtr associated with the given object key. +// Returns true if the data is found, and false otherwise. bool GetObjectValue(const std::string &obj_key, ValuePtr *const data); +// Set the FuncGraphPtr associated with the given object key. void SetObjGraphValue(const std::string &obj_key, const FuncGraphPtr &data); +// Get a mapping of object keys to lists of FuncGraphPtrs. const mindspore::HashMap> &GetObjGraphs(); +// Get a list of object keys for the provided Python object. std::vector GetObjKey(const py::object &obj); + +// Determine the type of the provided Python object. ResolveTypeDef GetObjType(const py::object &obj); + +// Determine the type of a class instance Python object. ClassInstanceTypeDef GetClassInstanceType(const py::object &obj); +// Check if the provided Python object is an instance of a cell. bool IsCellInstance(const py::object &obj); + +// Create a Python object of the specified type with the given arguments and keyword arguments. py::object CreatePythonObject(const py::object &type, const py::tuple &args_kwargs); + +// Call a Python script (function) with the provided arguments and keyword arguments. py::object CallPythonScript(const py::object &script, const py::tuple &args_kwargs); + +// Ensure that the given FuncGraph has a proper name for referencing. void MakeProperNameToFuncGraph(const FuncGraphPtr &func_graph, std::string name); + +// Convert a Python object to a MindSpore ValuePtr. ValuePtr PyDataToValue(const py::object &obj); + +// Clear the object cache, removing all cached objects. void ClearObjectCache(); + } // namespace data_converter +// Parse and return the MindSpore ClassPtr for the given Python class object. ClassPtr ParseDataClass(const py::object &cls_obj); + +// Convert the given Python object to a Bprop cut FuncGraph. FuncGraphPtr ConvertToBpropCut(const py::object &obj); +// Clear the mapping of data classes to MindSpore classes. void CleanDataClassToClassMap(); } // namespace parse diff --git a/mindspore/ccsrc/pipeline/jit/parse/function_block.cc b/mindspore/ccsrc/pipeline/jit/parse/function_block.cc index 9f94b1c42c7..0551b8b7135 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/function_block.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/function_block.cc @@ -244,30 +244,57 @@ AnfNodePtr FunctionBlock::MakeResolveClassMember(const std::string &attr) { MS_LOG(DEBUG) << "name_space: " << name_space->ToString() << ", symbol: " << symbol->ToString(); return MakeResolve(name_space, symbol); } - +// GetResolveNode function +// Creates a Resolve node for the given Python tuple 'info'. +// Parameters: +// - info: A Python tuple containing namespace and symbol information. +// Returns: +// An AnfNodePtr representing the Resolve node. AnfNodePtr FunctionBlock::GetResolveNode(const py::tuple &info) { constexpr size_t namespace_index = 0; constexpr size_t symbol_index = 1; + + // Create a NameSpacePtr from the namespace information. NameSpacePtr name_space = std::make_shared(RESOLVE_NAMESPACE_NAME_SYMBOL_STR, info[namespace_index]); + + // Create a SymbolPtr from the symbol information. SymbolPtr symbol = std::make_shared(info[symbol_index].cast()); + + // Create and return the Resolve node. return MakeResolve(name_space, symbol); } +// HandleNamespaceInfo function +// Handles namespace information in the given Python tuple 'info'. +// Parameters: +// - info: A Python tuple containing namespace and symbol information. +// Returns: +// An AnfNodePtr representing the Resolve node. AnfNodePtr FunctionBlock::HandleNamespaceInfo(const py::tuple &info) { constexpr size_t namespace_index = 0; constexpr size_t symbol_index = 1; constexpr size_t namespace_info_size = 2; + + // Check if the tuple has the correct size. if (info.size() != namespace_info_size) { MS_EXCEPTION(NameError) << "namespace info size should be 2, but got " << info.size(); } - // If namespace is None, the symbol is an undefined name. + // If the namespace is None, it means the symbol is an undefined name. if (info[namespace_index].is_none()) { MS_EXCEPTION(NameError) << info[symbol_index].cast(); } + + // Get and return the Resolve node. return GetResolveNode(info); } +// HandleBuiltinNamespaceInfo function +// Handles builtin namespace information in the given Python tuple 'info'. +// Parameters: +// - info: A Python tuple containing namespace, symbol, value, and flag information. +// Returns: +// An AnfNodePtr representing the Resolve node. AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) { constexpr size_t closure_info_size = 2; constexpr size_t namespace_info_size = 4; @@ -275,22 +302,28 @@ AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) { constexpr size_t symbol_index = 1; constexpr size_t value_index = 2; constexpr size_t flag_index = 3; + + // Check if the tuple has the correct size. if (info.size() != closure_info_size && info.size() != namespace_info_size) { MS_EXCEPTION(NameError) << "namespace info size should be 2 or 4, but got " << info.size(); } // Handle closure namespace info. if (info.size() == closure_info_size) { - // If namespace is None, the symbol is an undefined name. + // If the namespace is None, it means the symbol is an undefined name. if (info[namespace_index].is_none()) { MS_EXCEPTION(NameError) << info[symbol_index].cast(); } + + // Get and return the Resolve node. return GetResolveNode(info); } // Handle global namespace info. auto resolved_node = GetResolveNode(info); auto syntax_support = info[flag_index].cast(); + + // Set interpret flags based on syntax support. if (syntax_support != SYNTAX_SUPPORTED) { resolved_node->set_interpret(true); if (syntax_support == SYNTAX_UNSUPPORTED_INTERNAL_TYPE) { @@ -300,18 +333,31 @@ AnfNodePtr FunctionBlock::HandleBuiltinNamespaceInfo(const py::tuple &info) { resolved_node->set_interpret_special_type(true); } } + + // Create a Symbol from the symbol information. SymbolPtr symbol = std::make_shared(info[symbol_index].cast()); + + // Get the Python object and add it as a global Python parameter. py::object py_obj = info[value_index]; AddGlobalPyParam(symbol->name(), py_obj); + + // Log the added global Python symbol. MS_LOG(INFO) << "[" << func_graph()->ToString() << "] Added global python symbol: {" << symbol->name() << " : " << py::str(py_obj) << "}"; + return resolved_node; } -// Make a resolve node for symbol string +// MakeResolveSymbol function +// Creates a Resolve node for the given symbol string 'value'. +// Parameters: +// - value: A symbol string. +// Returns: +// An AnfNodePtr representing the Resolve node. AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) { MS_LOG(DEBUG) << "value: " << value; - // The prefix of value is "self.". + + // Check if the symbol string starts with "self.". if (value.compare(0, strlen("self"), "self") == 0) { auto start = value.find_first_of('.') + 1; if (start >= value.size()) { @@ -319,14 +365,18 @@ AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) { return nullptr; } auto bits_str = value.substr(start); + + // Create and return a ResolveClassMember node. return MakeResolveClassMember(bits_str); } + auto ast = parser_.ast(); MS_EXCEPTION_IF_NULL(ast); - // The fallback feature is enabled in default. - // Not support change the flag during the process is alive. + // Use fallback feature if enabled. static const auto use_fallback = (parser_.support_fallback() != "0"); + + // Choose the appropriate parsing method based on the fallback feature. if (!use_fallback) { py::tuple namespace_info = ast->CallParserObjMethod(PYTHON_PARSE_GET_NAMESPACE_SYMBOL, value); return HandleNamespaceInfo(namespace_info); @@ -335,42 +385,84 @@ AnfNodePtr FunctionBlock::MakeResolveSymbol(const std::string &value) { return HandleBuiltinNamespaceInfo(namespace_info); } } - +// MakeResolveOperation function +// This function creates a resolve operation for a given value. +// Parameters: +// - value: A string representing the value to be resolved. +// Returns: +// - An AnfNodePtr representing the resolved operation. AnfNodePtr FunctionBlock::MakeResolveOperation(const std::string &value) { auto ast = parser_.ast(); MS_EXCEPTION_IF_NULL(ast); + + // Call Python AST parsing function to get the operation namespace symbol. py::tuple namespace_var = ast->CallParseModFunction(PYTHON_PARSE_GET_OPERATION_NAMESPACE_SYMBOL, value); const size_t namespace_var_size = 2; if (namespace_var.size() < namespace_var_size) { MS_EXCEPTION(NameError) << "namespace_var is less than 2"; } + + // Create a NameSpacePtr and a SymbolPtr from the parsed values. NameSpacePtr name_space = std::make_shared(RESOLVE_NAMESPACE_NAME_COMMON_OPS, namespace_var[0]); SymbolPtr symbol = std::make_shared(namespace_var[1].cast()); MS_LOG(DEBUG) << "name_space: " << name_space->ToString() << ", symbol: " << symbol->ToString(); + + // Create and return the resolved operation. return MakeResolve(name_space, symbol); } +// MakeResolve function +// This function creates a resolve operation given a NameSpacePtr and a SymbolPtr. +// Parameters: +// - name_space: A NameSpacePtr representing the namespace of the resolved symbol. +// - resolve_symbol: A SymbolPtr representing the symbol to be resolved. +// Returns: +// - An AnfNodePtr representing the resolved operation. AnfNodePtr FunctionBlock::MakeResolve(const NameSpacePtr &name_space, const SymbolPtr &resolve_symbol) { MS_LOG(DEBUG) << "MakeResolve for " << (name_space ? (std::string)py::str(name_space->obj()) : "null namespace") << " , " << (resolve_symbol ? (std::string)resolve_symbol->symbol() : "null resolve symbol."); + + // Create ValueNodePtrs for the name_space and resolve_symbol. ValueNodePtr module_node = NewValueNode(name_space); ValueNodePtr symbol_node = NewValueNode(resolve_symbol); + + // Create a new CNode representing the resolve operation. auto node = func_graph_->NewCNodeInOrder({NewValueNode(prim::kPrimResolve), module_node, symbol_node}); + return node; } +// MakeInterpret function +// This function creates an interpret operation given a script text, global_dict_node, local_dict_node, and orig_node. +// Parameters: +// - script_text: A string representing the script text to be interpreted. +// - global_dict_node: An AnfNodePtr representing the global dictionary node. +// - local_dict_node: An AnfNodePtr representing the local dictionary node. +// - orig_node: An AnfNodePtr representing the original node for interpretation. +// Returns: +// - An AnfNodePtr representing the interpret operation. AnfNodePtr FunctionBlock::MakeInterpret(const std::string &script_text, const AnfNodePtr &global_dict_node, const AnfNodePtr &local_dict_node, const AnfNodePtr &orig_node) { MS_LOG(DEBUG) << "MakeInterpret for " << script_text; + + // Create a ScriptPtr from the script text. ScriptPtr script = std::make_shared