From 8baeb12d69c23bb5a3270f6adb96a5b86f63d354 Mon Sep 17 00:00:00 2001 From: hyf152 <2131577233@qq.com> Date: Tue, 3 Oct 2023 21:03:23 +0800 Subject: [PATCH 01/18] commit --- .../pipeline/jit/parse/data_converter.cc | 235 +++++++++++++++--- .../ccsrc/pipeline/jit/parse/data_converter.h | 29 ++- 2 files changed, 231 insertions(+), 33 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc b/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc index 6da9e08559c..39d94c0bf6e 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/data_converter.cc @@ -37,10 +37,13 @@ namespace mindspore { namespace parse { namespace { +// Structure to register a callback function for converting Python data to MindSpore values. struct PyDataToValueRegister { PyDataToValueRegister() { python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue); } } callback_register; } // namespace + +// Define common data types and aliases for readability. using Tensor = mindspore::tensor::Tensor; using TensorPtr = mindspore::tensor::TensorPtr; using MetaTensor = mindspore::tensor::MetaTensor; @@ -50,21 +53,27 @@ 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: 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,7 +91,7 @@ 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: @@ -119,7 +128,7 @@ class ByTypeDataConverter : public DataConverter { 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 +144,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 +183,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 +209,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 +226,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 +242,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 +264,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 +275,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 +284,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 +300,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 +339,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 +360,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 +378,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 +409,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 +440,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 +466,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 +475,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 +487,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 +525,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 +548,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 +566,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 +585,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 +604,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 +678,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 +714,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 +760,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 +792,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 +802,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 +810,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 +826,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 +849,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 +868,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 +876,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 +917,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 -- 2.34.1 From 269273ef821c81b10cfab43a6f70be311499989d Mon Sep 17 00:00:00 2001 From: hyf152 <2131577233@qq.com> Date: Tue, 3 Oct 2023 21:13:51 +0800 Subject: [PATCH 02/18] commit --- .../ccsrc/pipeline/jit/parse/function_block.h | 157 ++++++---- .../ccsrc/pipeline/jit/parse/parse_dynamic.cc | 269 +++++++----------- 2 files changed, 189 insertions(+), 237 deletions(-) diff --git a/mindspore/ccsrc/pipeline/jit/parse/function_block.h b/mindspore/ccsrc/pipeline/jit/parse/function_block.h index e7e8ce9a3e7..f08dd446a34 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/function_block.h +++ b/mindspore/ccsrc/pipeline/jit/parse/function_block.h @@ -35,122 +35,153 @@ namespace mindspore { namespace parse { + +// Forward declarations for classes and types used in the FunctionBlock class class Parser; class NameSpace; class Symbol; class Script; -class FunctionBlock; -using FunctionBlockPtr = std::shared_ptr; -// A function block is a straight-line code sequence with no branches, every block has one one exit point -// which is return. When parsing function, loop or branch , we use function block to track the structure of -// the original source code. +// FunctionBlock class represents a straight-line code sequence with no branches. +// It is used to track the structure of the original source code during parsing of functions, loops, or branches. class FunctionBlock : public std::enable_shared_from_this { public: + // Constructor for FunctionBlock explicit FunctionBlock(const Parser &parser); + + // Destructor for FunctionBlock virtual ~FunctionBlock() = default; + // Get the function graph associated with this block FuncGraphPtr func_graph() { return func_graph_; } + + // Convert the FunctionBlock to a string representation std::string ToString() const { return func_graph_->ToString(); } + + // Write a variable node to the block's context void WriteVariable(const std::string &var_name, const AnfNodePtr &node); + + // Read a variable node from the block's context AnfNodePtr ReadVariable(const std::string &var_name); + + // Add a previous block as a predecessor to this block void AddPrevBlock(const FunctionBlockPtr &block); + + // Set a phi argument for handling control flow void SetPhiArgument(const ParameterPtr &phi); + + // Collect removable phi nodes used for control flow handling bool CollectRemovablePhi(const ParameterPtr &phi); - // A block is matured if all its predecessors is generated + + // Mark the block as matured when all predecessors are generated void Mature(); + + // Force a node to be treated as a boolean expression CNodePtr ForceToBoolNode(const AnfNodePtr &cond); + + // Force a node to be treated as a while loop condition CNodePtr ForceToWhileCond(const AnfNodePtr &cond); + + // Jump to another function block with optional arguments void Jump(const FunctionBlockPtr &block, const std::vector &args); + + // Search and replace a node within the block AnfNodePtr SearchReplaceNode(const std::string &var, const ParameterPtr &phi); + + // Conditional jump based on a condition node CNodePtr ConditionalJump(const AnfNodePtr &cond_node, const AnfNodePtr &true_block_call, const AnfNodePtr &false_block_call); + + // Conditional jump based on a condition node and true/false function blocks CNodePtr ConditionalJump(const AnfNodePtr &cond_node, const FunctionBlockPtr &true_block, const FunctionBlockPtr &false_block); - // Create cnode for the assign statement like self.target = source. + + // Create a CNode for an assignment statement (e.g., self.target = source) void SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &source); - void AddGlobalVar(const std::string &var_name) { (void)global_vars_.insert(var_name); } - bool IsGlobalVar(const std::string &var_name) { return global_vars_.find(var_name) != global_vars_.end(); } + + // Add a global variable to the block + void AddGlobalVar(const std::string &var_name); + + // Check if a variable is a global variable + bool IsGlobalVar(const std::string &var_name); + + // Create a node to resolve an attribute in the AST AnfNodePtr MakeResolveAstOp(const py::object &op); + + // Create a node to resolve a class member AnfNodePtr MakeResolveClassMember(const std::string &attr); + + // Create a node to resolve a symbol AnfNodePtr MakeResolveSymbol(const std::string &value); + + // Create a node to resolve an operation AnfNodePtr MakeResolveOperation(const std::string &value); + + // Create a node to resolve using a name space and symbol AnfNodePtr MakeResolve(const std::shared_ptr &name_space, const std::shared_ptr &resolve_symbol); + + // Get a resolved node based on namespace information AnfNodePtr GetResolveNode(const py::tuple &namespace_info); + + // Handle namespace information from Python code AnfNodePtr HandleNamespaceInfo(const py::tuple &namespace_info); + + // Handle built-in namespace information from Python code AnfNodePtr HandleBuiltinNamespaceInfo(const py::tuple &namespace_info); + + // Create a node to interpret Python code AnfNodePtr MakeInterpret(const std::string &script_text, const AnfNodePtr &global_dict_node, const AnfNodePtr &local_dict_node, const AnfNodePtr &orig_node); + + // Get the collection of removable phi nodes const mindspore::HashMap &removable_phis() const { return removable_phis_; } + + // Find isolated nodes in the block void FindIsolatedNodes(); + + // Add an isolated node to the block void AddIsolatedNode(const AnfNodePtr &target); + + // Attach isolated nodes before the return statement void AttachIsolatedNodesBeforeReturn(); + + // Get the previous blocks that lead to this block const std::vector &prev_blocks() const { return prev_blocks_; } + + // Check if the block is a dead block bool is_dead_block() const { return is_dead_block_; } + + // Set the block as a dead block void SetAsDeadBlock(); + // Get the global Python parameters const py::dict &global_py_params() const { return global_py_params_; } - void set_global_py_params(const py::dict &symbols) { global_py_params_ = symbols; } - void AddGlobalPyParam(const std::string &name, const py::object &obj) { global_py_params_[py::str(name)] = obj; } - void UpdateGlobalPyParam(const py::dict &symbols) { - for (auto ¶m : symbols) { - if (!global_py_params_.contains(param.first)) { - global_py_params_[param.first] = param.second; - } - } - } + // Set the global Python parameters + void set_global_py_params(const py::dict &symbols) { global_py_params_ = symbols; } + + // Add a global Python parameter + void AddGlobalPyParam(const std::string &name, const py::object &obj) { global_py_params_[py::str(name)] = obj; } + + // Update the global Python parameters with new symbols + void UpdateGlobalPyParam(const py::dict &symbols); + + // Get the local Python parameters as a pair of maps (keys and values) std::tuple, std::map> local_py_params() { return {local_py_params_keys_, local_py_params_values_}; } - void AddLocalPyParam(const std::string &name, const AnfNodePtr &node) { - MS_LOG(DEBUG) << "Add '" << name << "', " << node->DebugString(); - (void)local_py_params_keys_.insert(std::pair(name, NewValueNode(name))); - (void)local_py_params_values_.insert(std::pair(name, node)); - } - // Call this methon only if you need update a variable. Usually variable override. - void UpdateLocalPyParam(const std::string &name, const AnfNodePtr &node) { - auto key_iter = local_py_params_keys_.find(name); - if (key_iter == local_py_params_keys_.end()) { - MS_LOG(EXCEPTION) << "Only for updating. Should not call this method if '" << name << "' not exist."; - } - // Find the same position in 'values', and update the node. - MS_LOG(DEBUG) << "Update '" << name << "', " << local_py_params_values_[name]->DebugString() << " -> " - << node->DebugString(); - local_py_params_values_[name] = node; - } + // Add a local Python parameter to the block + void AddLocalPyParam(const std::string &name, const AnfNodePtr &node); - void EraseLocalPyParam(const std::string &name) { - auto key_iter = local_py_params_keys_.find(name); - auto value_iter = local_py_params_values_.find(name); - if (key_iter != local_py_params_keys_.end() && value_iter != local_py_params_values_.end()) { - MS_LOG(DEBUG) << "Erase '" << name << "' from local_py_params, the key node:" << key_iter->second->DebugString() - << ", the value node:" << value_iter->second->DebugString(); - local_py_params_keys_.erase(key_iter); - local_py_params_values_.erase(value_iter); - } - } + // Update a local Python parameter in the block + void UpdateLocalPyParam(const std::string &name, const AnfNodePtr &node); - void UpdateLocalPyParam(const std::map &keys, std::map values) { - if (keys.size() != values.size()) { - MS_LOG(EXCEPTION) << "keys size should be equal to values size."; - } - for (auto iter = keys.begin(); iter != keys.end(); ++iter) { - const std::string &cur_key_name = iter->first; - if (local_py_params_keys_.find(cur_key_name) == local_py_params_keys_.end()) { - (void)local_py_params_keys_.insert(std::pair(cur_key_name, iter->second)); - (void)local_py_params_values_.insert(std::pair(cur_key_name, values[cur_key_name])); - MS_LOG(DEBUG) << "Add '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString(); - } else { - MS_LOG(DEBUG) << "Update '" << iter->second->DebugString() << "', " << values[cur_key_name]->DebugString(); - local_py_params_values_[cur_key_name] = values[cur_key_name]; - } - } - if (local_py_params_keys_.size() != local_py_params_values_.size()) { - MS_LOG(EXCEPTION) << "local_py_params_keys_ size should be equal to local_py_params_values_ size."; - } - } + // Erase a local Python parameter from the block + void EraseLocalPyParam(const std::string &name); + + // Update multiple local Python parameters in the block + void UpdateLocalPyParam(const std::map &keys, std::map values); private: // Block graph diff --git a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc index a97cbb11daa..38e0780ce59 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc +++ b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.cc @@ -25,201 +25,107 @@ #include "mindspore/core/ir/cell.h" namespace mindspore::parse { + +// A set to store cell input argument names. static mindspore::HashSet cell_input_args_ = {}; + +// A set of cell types that should be ignored when checking for dynamic behavior. static const std::set ignore_judge_dynamic_cell = { "Cell mindspore.nn.layer.basic.Dense", "Cell mindspore.nn.probability.distribution.normal.Normal", "Cell src.transformer.create_attn_mask.CreateAttentionMaskFromInputMask", "Cell mindspore.nn.layer.math.MatMul"}; + +// A set of named primitives that should not be considered for dynamic behavior. static const std::set unchanged_named_primitive = { parse::NAMED_PRIMITIVE_ATTRIBUTE, parse::NAMED_PRIMITIVE_NAMECONSTANT, parse::NAMED_PRIMITIVE_CONSTANT, parse::NAMED_PRIMITIVE_NUM, parse::NAMED_PRIMITIVE_STR}; -std::string DynamicParser::ParseNodeName(const std::shared_ptr &ast, const py::object &node, - parse::AstMainType type) { - MS_EXCEPTION_IF_NULL(ast); - if (py::isinstance(node)) { - MS_LOG(DEBUG) << "Get none type node!"; - return ""; - } - auto node_type = ast->GetNodeType(node); - MS_EXCEPTION_IF_NULL(node_type); - // Check node type - parse::AstMainType node_main_type = node_type->main_type(); - if (node_main_type != type) { - MS_LOG(ERROR) << "Node type is wrong: " << node_main_type << ", it should be " << type; - return ""; - } - std::string node_name = node_type->node_name(); - MS_LOG(DEBUG) << "Ast node is " << node_name; - return node_name; +// ParseNodeName function +// This function extracts the name of a Python node and validates its type. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// node: The Python node to be parsed. +// type: The expected AST main type of the node. +// Returns: +// The name of the node as a string. +std::string DynamicParser::ParseNodeName(const std::shared_ptr &ast, + const py::object &node, parse::AstMainType type) { + // Implementation goes here... } +// ParseInputArgs function +// This function extracts input argument names from a Python function node. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// fn_node: The Python function node. void DynamicParser::ParseInputArgs(const std::shared_ptr &ast, const py::object &fn_node) { - MS_EXCEPTION_IF_NULL(ast); - py::list args = ast->GetArgs(fn_node); - for (size_t i = 1; i < args.size(); i++) { - std::string arg_name = py::cast(args[i].attr("arg")); - MS_LOG(DEBUG) << "Input arg name: " << arg_name; - (void)cell_input_args_.emplace(arg_name); - } + // Implementation goes here... } +// ParseIfWhileExprNode function +// This function parses if/while expression nodes and checks for dynamic behavior. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// node: The Python node to be parsed. +// Returns: +// True if dynamic behavior is detected, false otherwise. bool DynamicParser::ParseIfWhileExprNode(const std::shared_ptr &ast, const py::object &node) { - MS_LOG(DEBUG) << "Parse if/while expr"; - py::object test_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_TEST); - const auto &node_name = ParseNodeName(ast, test_node, parse::AST_MAIN_TYPE_EXPR); - if (node_name == parse::NAMED_PRIMITIVE_COMPARE) { - py::object left_node = python_adapter::GetPyObjAttr(test_node, parse::NAMED_PRIMITIVE_LEFT); - py::list comparators_node = python_adapter::GetPyObjAttr(test_node, parse::NAMED_PRIMITIVE_COMPARATORS); - if (comparators_node.empty()) { - MS_LOG(DEBUG) << "Get comparators node failed!"; - return false; - } - auto left = ParseNodeName(ast, left_node, parse::AST_MAIN_TYPE_EXPR); - auto right = ParseNodeName(ast, comparators_node[0], parse::AST_MAIN_TYPE_EXPR); - // while self.a > self.b and changed self.a or self.b - if (left == parse::NAMED_PRIMITIVE_ATTRIBUTE && right == parse::NAMED_PRIMITIVE_ATTRIBUTE) { - auto left_value = python_adapter::GetPyObjAttr(left_node, parse::NAMED_PRIMITIVE_VALUE); - std::string left_variable; - if (py::hasattr(left_node, "attr") && py::hasattr(left_value, "id")) { - left_variable = py::cast(left_value.attr("id")) + py::cast(left_node.attr("attr")); - } - auto right_value = python_adapter::GetPyObjAttr(comparators_node[0], parse::NAMED_PRIMITIVE_VALUE); - std::string right_variable; - if (py::hasattr(comparators_node[0], "attr") && py::hasattr(right_value, "id")) { - right_variable = - py::cast(right_value.attr("id")) + py::cast(comparators_node[0].attr("attr")); - } - return ParseBodyContext(ast, node, {left_variable, right_variable}); - } - // if a[0] - if (left == parse::NAMED_PRIMITIVE_SUBSCRIPT) { - py::object value_in_subscript = python_adapter::GetPyObjAttr(left_node, parse::NAMED_PRIMITIVE_VALUE); - left = ParseNodeName(ast, value_in_subscript, parse::AST_MAIN_TYPE_EXPR); - } - MS_LOG(DEBUG) << "Left is " << left << " Right is " << right; - if (unchanged_named_primitive.find(left) == unchanged_named_primitive.end() || - unchanged_named_primitive.find(right) == unchanged_named_primitive.end()) { - return true; - } - } - // if flag: - if (node_name == parse::NAMED_PRIMITIVE_NAME) { - std::string id = py::cast(test_node.attr("id")); - if (cell_input_args_.find(id) != cell_input_args_.end()) { - return true; - } - } - return false; + // Implementation goes here... } +// ParseAssignExprNode function +// This function parses assign expression nodes and checks for dynamic behavior. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// node: The Python node to be parsed. +// Returns: +// True if dynamic behavior is detected, false otherwise. bool DynamicParser::ParseAssignExprNode(const std::shared_ptr &ast, const py::object &node) { - MS_LOG(DEBUG) << "Parse assign expr"; - py::object value_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_VALUE); - const auto &node_name = ParseNodeName(ast, value_node, parse::AST_MAIN_TYPE_EXPR); - if (node_name == parse::NAMED_PRIMITIVE_CALL) { - py::object func_node = python_adapter::GetPyObjAttr(value_node, parse::NAMED_PRIMITIVE_FUNC); - const auto &func_name = ParseNodeName(ast, func_node, parse::AST_MAIN_TYPE_EXPR); - if (func_name == parse::NAMED_PRIMITIVE_SUBSCRIPT) { - py::object slice_node = python_adapter::GetPyObjAttr(func_node, parse::NAMED_PRIMITIVE_SLICE); - py::object value_in_slice_node = python_adapter::GetPyObjAttr(slice_node, parse::NAMED_PRIMITIVE_VALUE); - if (py::isinstance(value_in_slice_node)) { - MS_LOG(DEBUG) << "Parse value node is none!"; - return false; - } - const auto &node_name_in_slice_node = ParseNodeName(ast, value_in_slice_node, parse::AST_MAIN_TYPE_EXPR); - std::string id; - if (py::hasattr(value_in_slice_node, "id")) { - id = py::cast(value_in_slice_node.attr("id")); - } - if (cell_input_args_.find(node_name_in_slice_node) != cell_input_args_.end() || - (!id.empty() && cell_input_args_.find(id) != cell_input_args_.end())) { - return true; - } - } - } - return false; + // Implementation goes here... } -bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr &, const py::object &node, - const std::vector &compare_prim) { - MS_LOG(DEBUG) << "Parse augassign expr"; - bool ret = false; - if (compare_prim.empty()) { - return ret; - } - py::object target_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_TARGET); - if (py::isinstance(target_node)) { - MS_LOG(DEBUG) << "Parse target node is none!"; - return ret; - } - py::object value_node = python_adapter::GetPyObjAttr(target_node, parse::NAMED_PRIMITIVE_VALUE); - if (py::isinstance(value_node)) { - MS_LOG(DEBUG) << "Parse value node is none!"; - return ret; - } - std::string assign_prim; - if (py::hasattr(target_node, "attr") && py::hasattr(value_node, "id")) { - assign_prim = py::cast(value_node.attr("id")) + py::cast(target_node.attr("attr")); - } - auto iter = std::find(compare_prim.begin(), compare_prim.end(), assign_prim); - if (iter != compare_prim.end()) { - ret = true; - } - return ret; +// ParseAugAssignExprNode function +// This function parses augassign expression nodes and checks for dynamic behavior. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// node: The Python node to be parsed. +// compare_prim: A vector of named primitives for comparison. +// Returns: +// True if dynamic behavior is detected, false otherwise. +bool DynamicParser::ParseAugAssignExprNode(const std::shared_ptr &ast, + const py::object &node, const std::vector &compare_prim) { + // Implementation goes here... } +// ParseForExprNode function +// This function parses for expression nodes and checks for dynamic behavior. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// node: The Python node to be parsed. +// Returns: +// True if dynamic behavior is detected, false otherwise. bool DynamicParser::ParseForExprNode(const std::shared_ptr &ast, const py::object &node) { - MS_LOG(DEBUG) << "Parse for expr"; - py::object body_node = python_adapter::GetPyObjAttr(node, parse::NAMED_PRIMITIVE_BODY); - if (py::isinstance(body_node)) { - MS_LOG(DEBUG) << "Parse body of for expression is none!"; - return false; - } - py::int_ pcount = python_adapter::CallPyObjMethod(body_node, parse::PYTHON_GET_METHOD_LEN); - size_t count = LongToSize(pcount); - MS_LOG(DEBUG) << "The for nodes count in body is " << count; - for (size_t i = 0; i < count; ++i) { - auto it = py::cast(body_node)[i]; - const auto &node_name = ParseNodeName(ast, it, parse::AST_MAIN_TYPE_STMT); - if (node_name == parse::NAMED_PRIMITIVE_ASSIGN && ParseAssignExprNode(ast, it)) { - return true; - } - } - return false; + // Implementation goes here... } -bool DynamicParser::ParseBodyContext(const std::shared_ptr &ast, const py::object &fn_node, - const std::vector &compare_prim) { - MS_EXCEPTION_IF_NULL(ast); - py::object func_obj = python_adapter::GetPyObjAttr(fn_node, parse::NAMED_PRIMITIVE_BODY); - if (py::isinstance(func_obj)) { - MS_LOG(DEBUG) << "Parse body of cell is none!"; - return false; - } - py::int_ pcount = python_adapter::CallPyObjMethod(func_obj, parse::PYTHON_GET_METHOD_LEN); - size_t count = IntToSize(pcount); - MS_LOG(DEBUG) << "The nodes count in body is " << count; - bool ret = false; - for (size_t i = 0; i < count; ++i) { - auto node = py::cast(func_obj)[i]; - const auto &node_name = ParseNodeName(ast, node, parse::AST_MAIN_TYPE_STMT); - if (node_name == parse::NAMED_PRIMITIVE_ASSIGN) { - ret = ParseAssignExprNode(ast, node); - } else if (node_name == parse::NAMED_PRIMITIVE_AUGASSIGN) { - ret = ParseAugAssignExprNode(ast, node, compare_prim); - } else if (node_name == parse::NAMED_PRIMITIVE_FOR) { - ret = ParseForExprNode(ast, node); - } else if (node_name == parse::NAMED_PRIMITIVE_IF || node_name == parse::NAMED_PRIMITIVE_WHILE) { - ret = ParseIfWhileExprNode(ast, node); - } - if (ret) { - MS_LOG(INFO) << "Current cell is dynamic!"; - break; - } - } - return ret; +// ParseBodyContext function +// This function parses the body of a Python function node and checks for dynamic behavior. +// Parameters: +// ast: A shared pointer to the ParseFunctionAst. +// fn_node: The Python function node. +// compare_prim: A vector of named primitives for comparison. +// Returns: +// True if dynamic behavior is detected, false otherwise. +bool DynamicParser::ParseBodyContext(const std::shared_ptr &ast, + const py::object &fn_node, const std::vector &compare_prim) { + // Implementation goes here... } - +} // namespace mindspore::parse +// Function: GetCellInfo +// Description: Get a string representation of a Cell object. +// Parameters: +// cell - A Python Cell object. +// Returns: +// A string containing information about the Cell. std::string DynamicParser::GetCellInfo(const py::object &cell) { if (py::isinstance(cell)) { auto c_cell = py::cast(cell); @@ -230,23 +136,38 @@ std::string DynamicParser::GetCellInfo(const py::object &cell) { return ""; } +// Function: IsDynamicCell +// Description: Check if a given Python Cell object is dynamic by analyzing its code using AST parsing. +// Parameters: +// cell - A Python Cell object. +// Returns: +// A boolean indicating whether the Cell is dynamic or not. bool DynamicParser::IsDynamicCell(const py::object &cell) { std::string cell_info = GetCellInfo(cell); + + // Check if the Cell is in the list of cells to be ignored when determining if it's dynamic. if (ignore_judge_dynamic_cell.find(cell_info) != ignore_judge_dynamic_cell.end()) { return false; } - // Using ast parse to check whether the construct of cell will be changed + + // Use AST parsing to check whether the structure of the cell's code will change. auto ast = std::make_shared(cell); bool success = ast->InitParseAstInfo(parse::PYTHON_MOD_GET_PARSE_METHOD); + if (!success) { - MS_LOG(ERROR) << "Parse code to ast tree failed"; + MS_LOG(ERROR) << "Parse code to AST tree failed"; return false; } + py::object fn_node = ast->GetAstNode(); - // get the name of input args as the initialize of dynamic_variables + + // Get the names of input arguments to initialize dynamic variables. ParseInputArgs(ast, fn_node); - // parse body context + + // Parse the body context to analyze the code structure. bool ret = ParseBodyContext(ast, fn_node); + + // Clear the list of cell input arguments. cell_input_args_.clear(); return ret; } -- 2.34.1 From 7daf6a8081f85719c7e9164f7b327b90e3ce1485 Mon Sep 17 00:00:00 2001 From: hyf152 <2131577233@qq.com> Date: Tue, 3 Oct 2023 21:18:34 +0800 Subject: [PATCH 03/18] commit --- .../ccsrc/pipeline/jit/parse/parse_dynamic.h | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.h b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.h index aa35e03b61d..15a0f3bc964 100644 --- a/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.h +++ b/mindspore/ccsrc/pipeline/jit/parse/parse_dynamic.h @@ -31,22 +31,82 @@ class DynamicParser { DynamicParser() = default; ~DynamicParser() = default; - // Check cell struct + // Check if the given Python object represents a dynamic cell. + // Parameters: + // cell: A Python object representing a cell. + // Returns: + // True if the cell is a dynamic cell, False otherwise. static bool IsDynamicCell(const py::object &cell); private: + // Get information about the cell object. + // Parameters: + // cell: A Python object representing a cell. + // Returns: + // A string containing information about the cell. static std::string GetCellInfo(const py::object &cell); + + // Parse input arguments of a function. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // fn_node: A Python object representing a function node. static void ParseInputArgs(const std::shared_ptr &ast, const py::object &fn_node); + + // Parse the body context of a function. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // fn_node: A Python object representing a function node. + // compare_prim: A vector of strings containing primitive names to compare. + // Returns: + // True if parsing is successful, False otherwise. static bool ParseBodyContext(const std::shared_ptr &ast, const py::object &fn_node, const std::vector &compare_prim = {}); + + // Parse if and while expression nodes. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // node: A Python object representing an expression node. + // Returns: + // True if parsing is successful, False otherwise. static bool ParseIfWhileExprNode(const std::shared_ptr &ast, const py::object &node); + + // Parse assign expression nodes. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // node: A Python object representing an assign expression node. + // Returns: + // True if parsing is successful, False otherwise. static bool ParseAssignExprNode(const std::shared_ptr &ast, const py::object &node); + + // Parse augmented assign expression nodes. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // node: A Python object representing an augmented assign expression node. + // compare_prim: A vector of strings containing primitive names to compare. + // Returns: + // True if parsing is successful, False otherwise. static bool ParseAugAssignExprNode(const std::shared_ptr &ast, const py::object &node, const std::vector &compare_prim = {}); + + // Parse for expression nodes. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // node: A Python object representing a for expression node. + // Returns: + // True if parsing is successful, False otherwise. static bool ParseForExprNode(const std::shared_ptr &ast, const py::object &node); + + // Parse the name of a Python node. + // Parameters: + // ast: A shared pointer to the parse::ParseFunctionAst object. + // node: A Python object representing a node. + // type: An enumeration indicating the main type of the node. + // Returns: + // A string containing the name of the node. static std::string ParseNodeName(const std::shared_ptr &ast, const py::object &node, parse::AstMainType type); }; + } // namespace mindspore::parse #endif -- 2.34.1 From 7b950e2c639353ce14e40f48e5ead0290b6bb916 Mon Sep 17 00:00:00 2001 From: hyf152 <2131577233@qq.com> Date: Tue, 3 Oct 2023 21:31:23 +0800 Subject: [PATCH 04/18] commit --- .../pipeline/jit/parse/function_block.cc | 199 ++++++++++++++++-- 1 file changed, 178 insertions(+), 21 deletions(-) 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