1 /** 2 * This is the C++ adaptation and derivative work of Myia (https://github.com/mila-iqia/myia/). 3 * 4 * Copyright 2019-2021 Huawei Technologies Co., Ltd 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 #ifndef MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_ 20 #define MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_ 21 22 #include <vector> 23 #include <string> 24 #include <map> 25 #include <set> 26 #include <unordered_map> 27 #include <memory> 28 #include <utility> 29 #include <tuple> 30 #include "pipeline/jit/parse/parse_base.h" 31 #include "utils/log_adapter.h" 32 #include "utils/ordered_set.h" 33 34 namespace mindspore { 35 namespace parse { 36 class Parser; 37 class NameSpace; 38 class Symbol; 39 class Script; 40 class FunctionBlock; 41 using FunctionBlockPtr = std::shared_ptr<FunctionBlock>; 42 43 // A function block is a straight-line code sequence with no branches, every block has one one exit point 44 // which is return. When parsing function, loop or branch , we use function block to track the structure of 45 // the original source code. 46 class FunctionBlock : public std::enable_shared_from_this<FunctionBlock> { 47 public: 48 explicit FunctionBlock(const Parser &parser); 49 virtual ~FunctionBlock() = default; 50 func_graph()51 FuncGraphPtr func_graph() { return func_graph_; } ToString()52 std::string ToString() const { return func_graph_->ToString(); } 53 void WriteVariable(const std::string &var_name, const AnfNodePtr &node); 54 AnfNodePtr ReadVariable(const std::string &var_name); 55 void AddPrevBlock(const FunctionBlockPtr &block); 56 void SetPhiArgument(const ParameterPtr &phi); 57 bool CollectRemovablePhi(const ParameterPtr &phi); 58 // A block is matured if all its predecessors is generated 59 void Mature(); 60 CNodePtr ForceToBoolNode(const AnfNodePtr &cond); 61 CNodePtr ForceToWhileCond(const AnfNodePtr &cond); 62 void Jump(const FunctionBlockPtr &block, const std::vector<AnfNodePtr> &args); 63 AnfNodePtr SearchReplaceNode(const std::string &var, const ParameterPtr &phi); 64 void ConditionalJump(AnfNodePtr condNode, const FunctionBlockPtr &trueBlock, const FunctionBlockPtr &falseBlock, 65 bool unroll_loop = true); 66 // Create cnode for the assign statement like self.target = source. 67 void SetStateAssign(const AnfNodePtr &target, const AnfNodePtr &source); AddGlobalVar(const std::string & var_name)68 void AddGlobalVar(const std::string &var_name) { (void)global_vars_.insert(var_name); } IsGlobalVar(const std::string & var_name)69 bool IsGlobalVar(const std::string &var_name) { return global_vars_.find(var_name) != global_vars_.end(); } 70 AnfNodePtr MakeResolveAstOp(const py::object &op); 71 AnfNodePtr MakeResolveClassMember(const std::string &attr); 72 AnfNodePtr MakeResolveSymbol(const std::string &value); 73 AnfNodePtr MakeResolveOperation(const std::string &value); 74 AnfNodePtr MakeResolve(const std::shared_ptr<NameSpace> &name_space, const std::shared_ptr<Symbol> &resolve_symbol); 75 AnfNodePtr HandleNamespaceInfo(const py::tuple &namespace_info); 76 AnfNodePtr MakeInterpret(const std::string &script_text, const AnfNodePtr &global_dict_node, 77 const AnfNodePtr &local_dict_node, const AnfNodePtr &orig_node); removable_phis()78 const std::unordered_map<ParameterPtr, AnfNodePtr> &removable_phis() const { return removable_phis_; } 79 void FindIsolatedNodes(); 80 void AddIsolatedNode(const AnfNodePtr &target); 81 void AttachIsolatedNodesBeforeReturn(); prev_blocks()82 const std::vector<FunctionBlock *> &prev_blocks() const { return prev_blocks_; } is_dead_block()83 bool is_dead_block() const { return is_dead_block_; } 84 void SetAsDeadBlock(); 85 global_py_params()86 py::dict &global_py_params() { return global_py_params_; } set_global_py_params(const py::dict & symbols)87 void set_global_py_params(const py::dict &symbols) { global_py_params_ = symbols; } AddGlobalPyParam(const std::string & name,const py::object & obj)88 void AddGlobalPyParam(const std::string &name, const py::object &obj) { global_py_params_[py::str(name)] = obj; } CopyGlobalPyParam(const py::dict & symbols)89 void CopyGlobalPyParam(const py::dict &symbols) { 90 for (auto ¶m : symbols) { 91 if (!global_py_params_.contains(param.first)) { 92 global_py_params_[param.first] = param.second; 93 } 94 } 95 } 96 local_py_params()97 std::tuple<std::vector<AnfNodePtr>, std::vector<AnfNodePtr>> local_py_params() { 98 return {local_py_params_keys_, local_py_params_values_}; 99 } AddLocalPyParam(const std::string & name,const AnfNodePtr & node)100 void AddLocalPyParam(const std::string &name, const AnfNodePtr &node) { 101 local_py_params_keys_.emplace_back(NewValueNode(name)); 102 local_py_params_values_.emplace_back(node); 103 } 104 105 private: 106 // Block graph 107 FuncGraphPtr func_graph_; 108 109 // Block parser 110 const Parser &parser_; 111 112 // A block is matured if all its prev_blocks is processed 113 bool matured_; 114 115 // Store the nest-level block. 116 // Refer to comments in Parser::func_block_list_; 117 std::vector<FunctionBlock *> prev_blocks_; 118 119 // Store args and variable's node, use a bool flag to indicate if the variable is used. 120 std::map<std::string, std::pair<AnfNodePtr, bool>> assigned_vars_; 121 122 // Map the parameter node to variable, it can be resolved if the block's predecessors are processed 123 std::map<ParameterPtr, std::string> phi_nodes_; 124 125 // Jumps map the successor block and the function call that perform jump 126 // Refer to comments in Parser::func_block_list_ that how to break the cyclic reference 127 std::map<FunctionBlock *, CNodePtr> jumps_; 128 129 // Keep all removable phis which will be removed in one pass. 130 std::unordered_map<ParameterPtr, AnfNodePtr> removable_phis_; 131 132 // Keep the map for the resolve node to the removable phi node. 133 // For the case that ReadVariable returns a phi node although this phi node 134 // generated in the prev block is identified as removable. The other blocks 135 // should find this phi node. 136 std::unordered_map<AnfNodePtr, ParameterPtr> resolve_to_removable_phis_; 137 138 // Hold declared global variables in function 139 std::set<std::string> global_vars_; 140 141 // Keep new made resolve symbol for the variable not found in vars_. 142 std::unordered_map<std::string, AnfNodePtr> var_to_resolve_; 143 144 // Collect all python symbols in the block. 145 // We treat both global symbols and local symbols declared previously as global symbols. 146 py::dict global_py_params_; 147 std::vector<AnfNodePtr> local_py_params_keys_; 148 std::vector<AnfNodePtr> local_py_params_values_; 149 150 // Isolated nodes. 151 OrderedSet<AnfNodePtr> isolated_nodes_; 152 153 // If a block can never be executed, it's prev blocks will be empty, so this block is a dead block. 154 // while x > 5: 155 // x = x - 2 156 // if x > 7 : 157 // break 158 // else : 159 // break 160 // x = x - 1 #This after block is a dead block 161 bool is_dead_block_{false}; 162 }; 163 164 class ScriptInfo { 165 public: ScriptInfo(const py::object & obj)166 explicit ScriptInfo(const py::object &obj) : py_obj_(obj) {} 167 ~ScriptInfo() = default; 168 // Key for user data. 169 constexpr static char key[] = "ScriptInfo"; 170 171 py::object py_obj_; 172 }; 173 } // namespace parse 174 } // namespace mindspore 175 176 #endif // MINDSPORE_CCSRC_PIPELINE_JIT_PARSE_FUNCTION_BLOCK_H_ 177