• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2023 Huawei Technologies Co., Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "pipeline/jit/pi/graph_capture/graph.h"
17 #include <set>
18 #include <algorithm>
19 #include <memory>
20 #include <string>
21 #include <regex>
22 #include <utility>
23 #include "pipeline/jit/pi/common.h"
24 
25 namespace mindspore {
26 namespace pijit {
Graph(PyCodeObject * co,PyObject * globals,const GraphJitConfig & conf)27 Graph::Graph(PyCodeObject *co, PyObject *globals, const GraphJitConfig &conf)
28     : ret_val_(nullptr),
29       generator_result_(nullptr),
30       co_(py::cast<py::object>(reinterpret_cast<PyObject *>(co))),
31       f_globals_(py::cast<py::object>(globals)),
32       conf_(conf),
33       guard_(nullptr),
34       prune_branch_count_(0) {
35   stop_trace_info_ = {-1, StopTraceReason::kNonStopTrace};
36   if (!co) {
37     frame_states_[0] = std::make_unique<FrameStates>();  // emplace empty frame
38     module_name_ = "";
39     return;
40   }
41   cfg_ = std::make_unique<CFG>(co);
42   cfg_->GenerateCFG();
43 
44   if (conf_.GetBoolConfig(GraphJitConfig::kPrintBB)) {
45     GRAPH_JIT_LOG_F("%s\n\n", cfg_->DumpBBs().c_str());
46   }
47   if (conf_.GetBoolConfig(GraphJitConfig::kPrintCFG)) {
48     cfg_->DumpCFGGraph();
49   }
50 
51   auto pyname = PyDict_GetItemString(globals, "__name__");
52   if (pyname) {
53     module_name_ = PyUnicode_AsUTF8(pyname);
54   } else {
55     module_name_ = "";
56     PyErr_Clear();
57   }
58 
59   if (conf_.GetBoolConfig(GraphJitConfig::kLoopUnrolling)) {
60     LoopFinder loop_finder(this);
61     loop_finder.FormSimpleLoopInfo();
62   }
63 }
64 
GetSideEffect() const65 const std::shared_ptr<SideEffect> &Graph::GetSideEffect() const { return side_effect_; }
SetSideEffect(const std::shared_ptr<SideEffect> & handler)66 void Graph::SetSideEffect(const std::shared_ptr<SideEffect> &handler) { side_effect_ = handler; }
67 
NewValueNode(AObject * obj_info,int op,int arg,const std::vector<ValueNode * > & inputs,const std::string & name)68 ValueNode *Graph::NewValueNode(AObject *obj_info, int op, int arg, const std::vector<ValueNode *> &inputs,
69                                const std::string &name) {
70   // when got a new object, check it's side-effect replaced node ......
71   MS_EXCEPTION_IF_CHECK_FAIL(!Opcode(op).IsCall(), "must not be call function opcode");
72   ValueNode *node = this->allocator().NewNode<ValueNode>(obj_info, op, arg, inputs);
73   node->SetName(name);
74   node->SetGraph(this);
75   ConstantInfo::CollectConstantInfo(node);
76   if (node->IsConstantValue() && obj_info && CheckConstPyObject(obj_info->GetPyObject().ptr())) {
77     node->SetOpcode(LOAD_CONST);
78     node->SetOparg(-1);
79     node->ClearInputs();
80   }
81   auto new_object = obj_info ? obj_info->GetPyObject().ptr() : nullptr;
82   if (new_object != nullptr && !CheckConstPyObject(new_object)) {  // literal not need track
83     this->side_effect_->data()->Track(new_object, node);
84   }
85   return node;
86 }
87 
NewCallNode(int op,int arg,const std::vector<ValueNode * > & inputs)88 CallNode *Graph::NewCallNode(int op, int arg, const std::vector<ValueNode *> &inputs) {
89   MS_EXCEPTION_IF_CHECK_FAIL(Opcode(op).IsCall(), "must be call function opcode");
90   CallNode *node = this->allocator().NewNode<CallNode>(op, arg, inputs);
91   node->SetGraph(this);
92   return node;
93 }
94 
95 /**
96  * FindLoopEnd, FindLoopBegin, reset break bci
97  * restore graph status. clean the variable that loop produced
98  * restore frame status of break bci that override by loop analyze
99  */
IsBreakAtLoop() const100 bool Graph::IsBreakAtLoop() const {
101   int break_bci = this->GetStopTraceBci();
102   if (break_bci == -1) {
103     return false;
104   }
105   const auto &instr = this->cfg_->instr_pool();
106   // find the last backward edge overlapping this break point
107   int res = break_bci;
108   for (int i = break_bci; i < SizeToInt(instr.size()); ++i) {
109     MS_EXCEPTION_IF_CHECK_FAIL(i == instr[i]->bci(), "!!!");
110     if (instr[i]->extra_jump() != nullptr) {
111       res = std::min(instr[i]->extra_jump()->bci(), res);
112     }
113   }
114   return res != break_bci;
115 }
116 
IsBreakAtLoopAfterUnrolling() const117 bool Graph::IsBreakAtLoopAfterUnrolling() const {
118   if (!Config().GetBoolConfig(GraphJitConfig::kLoopUnrolling)) {
119     return false;
120   }
121   if (GetStopTraceBci() == -1) {
122     return false;
123   }
124   if (traced_nodes_.empty()) {
125     return false;
126   }
127   if (GetStopTraceBci() > traced_nodes_.back()->bci()) {
128     return false;
129   }
130   bool break_with_loop_unroll = false;
131   for (auto node : traced_nodes_) {
132     if (node->bci() >= GetStopTraceBci()) {
133       break_with_loop_unroll |= node->GetBlock() != nullptr && node->GetBlock()->is_loop_body();
134     }
135   }
136   return break_with_loop_unroll;
137 }
138 
SetFrame(int bci,const FrameStates & f)139 void Graph::SetFrame(int bci, const FrameStates &f) {
140   // just set once, used to restore the first status if has a loop
141   auto &ptr = frame_states_[bci];
142   if (ptr == nullptr) {
143     ptr = std::make_unique<FrameStates>(f);
144   }
145 }
146 
GetFrame(int bci) const147 const FrameStates &Graph::GetFrame(int bci) const {
148   auto iter = frame_states_.find(bci);
149   MS_EXCEPTION_IF_CHECK_FAIL(iter != frame_states_.end(), "can't find frame status");
150   return *(iter->second);
151 }
152 
CheckObjPtr(ValueNode * node)153 static bool CheckObjPtr(ValueNode *node) {
154   return node->GetVobj() == nullptr || node->GetVobj()->GetPyObject().ptr() == nullptr;
155 }
156 
157 TracePtr GetTrace(ValueNode *node, bool strict, bool print, int depth, int max_depth);
PrepareTraceParam(ValueNode * node,TraceVector * tv,int depth,int max_depth,bool * has_unsupported,bool strict,bool print)158 static bool PrepareTraceParam(ValueNode *node, TraceVector *tv, int depth, int max_depth, bool *has_unsupported,
159                               bool strict, bool print) {
160   const std::vector<ValueNode *> &inputs = node->getInputs();
161   for (auto it : inputs) {
162     auto t = GetTrace(it, strict, print, depth + 1, max_depth);
163     if (t == nullptr) {
164       if (it->GetTrace() != nullptr) {
165         tv->push_back(it->GetTrace());
166       }
167       return false;
168     } else if (t->GetTraceType() == TraceType::Unsupported) {
169       *has_unsupported = true;
170     }
171     tv->push_back(t);
172   }
173   return !CheckObjPtr(node);
174 }
175 
CheckDepth(int depth,int max_depth)176 static bool CheckDepth(int depth, int max_depth) { return depth < max_depth || max_depth == -1; }
177 
CheckDepthForTrace(TracePtr * ret,ValueNode * node,int depth,int max_depth)178 static bool CheckDepthForTrace(TracePtr *ret, ValueNode *node, int depth, int max_depth) {
179   if (!CheckDepth(depth, max_depth)) {
180     MS_LOG(DEBUG) << "too deep trace for guard";
181     return false;
182   }
183   auto ct = node->GetTrace();
184   if (ct != nullptr) {
185     if (ct->GetDepth() + depth > max_depth && max_depth != -1) {
186       MS_LOG(DEBUG) << "too deep trace for guard";
187       return false;
188     } else {
189       *ret = ct;
190       return false;
191     }
192   }
193   return true;
194 }
195 
CacheTrace(ValueNode * node,TracePtr ret,bool strict,TraceVector tv,int opcode,int oparg,PyObject * obj)196 static TracePtr CacheTrace(ValueNode *node, TracePtr ret, bool strict, TraceVector tv, int opcode, int oparg,
197                            PyObject *obj) {
198   if (ret == nullptr && !strict) {
199     return std::make_shared<UnsupportedTrace>(obj, tv, opcode, oparg);
200   } else {
201     if (ret != nullptr) {
202       node->SetTrace(ret);
203     }
204     return ret;
205   }
206 }
207 
GetTrace(ValueNode * node,bool strict,bool print,int depth,int max_depth)208 TracePtr GetTrace(ValueNode *node, bool strict, bool print, int depth, int max_depth) {
209   TracePtr ret = nullptr;
210   if (!CheckDepthForTrace(&ret, node, depth, max_depth)) {
211     return ret;
212   }
213   if (node->GetType() == AbstractNode::Type::Call) {
214     Graph *sub_graph = static_cast<CallNode *>(node)->GetSubGraph();
215     if (sub_graph && sub_graph->GetRetVal() != nullptr) {
216       return GetTrace(sub_graph->GetRetVal(), strict, print, depth, max_depth);
217     }
218   }
219 
220   PyObject *obj = node->GetVobj() ? node->GetVobj()->GetPyObject().ptr() : nullptr;
221   int opcode = node->GetOpcode();
222   int oparg = node->GetOparg();
223   const std::string &name = node->GetName();
224   const char *module_name = node->GetGraph() ? node->GetGraph()->GetModuleName() : "";
225 
226   TraceVector tv;
227   bool has_unsupported = false;
228   if (!PrepareTraceParam(node, &tv, depth, max_depth, &has_unsupported, strict, print)) {
229     return strict ? nullptr : std::make_shared<UnsupportedTrace>(nullptr, tv, opcode, oparg);
230   }
231   switch (node->GetType()) {
232     case AbstractNode::Type::Value:
233       if (!has_unsupported) {
234         ret = CreateOpTrace(obj, opcode, oparg, tv, module_name, name, strict, print);
235       }
236       break;
237     case AbstractNode::Type::Call:
238       if (!has_unsupported) {
239         const std::string &func_name = node->input(0)->GetName();
240         ret = CreateOpTrace(obj, opcode, oparg, tv, module_name, func_name, strict, print);
241       }
242       break;
243     case AbstractNode::Type::Param:
244     case AbstractNode::Type::CellVar: /* fall-through */
245     case AbstractNode::Type::FreeVar:
246       if (oparg == -1) {
247         return nullptr;
248       }
249       ret = std::make_shared<RootTrace>(obj, mindspore::pijit::TraceType::Param, oparg, name);
250       break;
251     case AbstractNode::Type::kUnbound:
252     default:
253       break;
254   }
255   return CacheTrace(node, ret, strict, tv, opcode, oparg, obj);
256 }
257 
GuardValueNode(ValueNode * node,GuardLevel level)258 bool Graph::GuardValueNode(ValueNode *node, GuardLevel level) {
259   if (node->IsConstantValue()) {
260     return true;
261   }
262   TracePtr tr = this->TraceValueNode(node);
263   if (tr == nullptr) {
264     return false;
265   }
266   bool ret = guard_->GetGuard()->GuardOn(tr, level);
267   if (level == GuardLevel::GEqual || level == GuardLevel::GId) {
268     node->SetConstantValue(ret);
269   }
270   return ret;
271 }
272 
TraceValueNode(ValueNode * node,int max_trace_depth)273 TracePtr Graph::TraceValueNode(ValueNode *node, int max_trace_depth) {
274   AObject *vo = node->GetVobj();
275   if (guard_ == nullptr || !vo || vo->GetPyObject().ptr() == nullptr) {
276     return nullptr;
277   }
278   if (max_trace_depth < 0) {
279     max_trace_depth = Config().getIntConfig(GraphJitConfig::GraphJitConfig::kMaxTraceDepth);
280   }
281   return GetTrace(node, Config().GetBoolConfig(GraphJitConfig::kStrictTrace),
282                   Config().GetBoolConfig(GraphJitConfig::kPrintGuard), 0, max_trace_depth);
283 }
284 
CollectAliveNode(int bci,std::vector<int> * ids,BitMap * map) const285 std::vector<ValueNode *> Graph::CollectAliveNode(int bci, std::vector<int> *ids, BitMap *map) const {
286   std::vector<ValueNode *> result;
287   if (bci == -1) {
288     result = {this->GetRetVal()};
289   } else {
290     BitMap alive = this->GetCFG()->GetLiveness()->CollectAlive(bci);
291     result = CollectAliveNode(this->GetFrame(bci), &alive, ids);
292     if (map != nullptr) {
293       *map = std::move(alive);
294     }
295   }
296   if (GetSideEffect()->IsEmpty()) {
297     return result;
298   }
299   // alive locals must be original node
300   result.insert(result.end(), side_effect_->GetRequiredNodes().begin(), side_effect_->GetRequiredNodes().end());
301   for (auto &node : result) {
302     auto new_node = this->GetSideEffect()->GetSource(node);
303     if (new_node->GetOpcode() == LOAD_ATTR) {  // transform the alive attribute source
304       auto &attr_source = new_node->getInputs()[0];
305       attr_source = this->GetSideEffect()->GetSource(attr_source);
306     }
307     node = new_node;
308   }
309   return result;
310 }
311 
CollectAliveNode(const FrameStates & last_frame,BitMap * alive,std::vector<int> * ids)312 std::vector<ValueNode *> Graph::CollectAliveNode(const FrameStates &last_frame, BitMap *alive, std::vector<int> *ids) {
313   std::vector<ValueNode *> outputs = last_frame.GetStacks();
314   // collect alive locals
315   for (BitMap::Iter iter(alive, true), end(alive, false); iter != end; ++iter) {
316     size_t i = *iter;
317     // exclude undefined locals
318     if (last_frame.Local(i) != &ValueNode::kUnboundLocal) {
319       if (ids != nullptr) {
320         ids->push_back(i);
321       }
322       outputs.push_back(last_frame.Local(i));
323     } else {
324       alive->Clear(i);
325     }
326   }
327   return outputs;
328 }
329 
GuardSequenceNodeLength(ValueNode * sequence_node,Py_ssize_t sequence_size)330 bool Graph::GuardSequenceNodeLength(ValueNode *sequence_node, Py_ssize_t sequence_size) {
331   if (sequence_node->IsConstantValue()) {
332     return true;
333   }
334   const auto &cnst = sequence_node->GetConstantInfo();
335   if (cnst != nullptr && cnst->len() != -1) {
336     MS_EXCEPTION_IF_CHECK_FAIL(sequence_size == cnst->len(), "error sequence length");
337     return true;
338   }
339   TracePtr tr = this->TraceValueNode(sequence_node);
340   if (tr == nullptr) {
341     return false;
342   }
343   const auto &guard = this->GetGuard()->GetGuard();
344   bool strict = this->Config().GetBoolConfig(GraphJitConfig::kStrictTrace);
345 
346   PyObject *builtin_len = PyDict_GetItemString(PyEval_GetBuiltins(), "len");
347   MS_EXCEPTION_IF_NULL(builtin_len);
348   TracePtr len_func = CreateOpTrace(builtin_len, LOAD_CONST, -1, {}, "", "", strict);
349   TracePtr len_trace = CreateOpTrace(py::int_(sequence_size).ptr(), CALL_FUNCTION, 1, {len_func, tr}, "", "", strict);
350   guard->GuardOn(len_trace, GuardLevel::GEqual, false);
351 
352   sequence_node->MakeConstantInfo()->set_len(sequence_size);
353   return true;
354 }
355 
GuardType(ValueNode * node)356 bool Graph::GuardType(ValueNode *node) {
357   if (node->IsConstantValue()) {
358     return true;
359   }
360   const auto &cnst = node->GetConstantInfo();
361   if (cnst != nullptr && cnst->type() != nullptr) {
362     return true;
363   }
364   TracePtr tr = this->TraceValueNode(node);
365   if (tr == nullptr) {
366     return false;
367   }
368   bool ret = guard_->GetGuard()->GuardOn(tr, mindspore::pijit::GuardLevel::GType);
369   node->MakeConstantInfo()->set_type(node->GetVobj()->GetTypeObject());
370   return ret;
371 }
372 
SkipGuardInlinedFunc(ValueNode * func_node)373 static bool SkipGuardInlinedFunc(ValueNode *func_node) {
374   if (func_node->IsConstantValue()) {
375     return true;
376   }
377   AObject::Type value_type = func_node->GetVobj()->GetType();
378   if (func_node->GetOpcode() == LOAD_ATTR) {
379     AObject *src_info = func_node->input(0)->GetVobj();
380     if (src_info->GetType() == AObject::kTypeTensor && value_type == AObject::kTypeBoundMethod) {
381       // function from Tensor
382       return true;
383     }
384   }
385   return false;
386 }
387 
GuardInlinedFunc(CallNode * call_node)388 bool Graph::GuardInlinedFunc(CallNode *call_node) {
389   if (SkipGuardInlinedFunc(call_node->input(0))) {
390     return true;
391   }
392   TracePtr tr = this->TraceValueNode(call_node->input(0));
393   if (tr == nullptr) {
394     return false;
395   }
396   const auto &guard = this->GetGuard()->GetGuard();
397   bool strict = this->Config().GetBoolConfig(GraphJitConfig::kStrictTrace);
398 
399   AObject *callable_info = call_node->input(0)->GetVobj();
400   AObject::Type func_type = callable_info->GetType();
401   PyObject *callable = callable_info->GetPyObject().ptr();
402   if (func_type == AObject::kTypeBoundMethod) {
403     PyObject *func = PyMethod_GET_FUNCTION(callable);
404     tr = CreateOpTrace(func, LOAD_ATTR, 0, {tr}, "", "__func__", strict);
405     guard->GuardOn(tr, GuardLevel::GId);
406   } else if (func_type == AObject::kTypeCell || func_type == AObject::kTypeAnyValue) {
407     guard->GuardOn(tr, GuardLevel::GType, false);
408     call_node->input(0)->MakeConstantInfo()->set_type(callable_info->GetTypeObject());
409   } else if (func_type == AObject::kTypeFunction) {
410     guard->GuardOn(tr, GuardLevel::GId);
411     call_node->input(0)->SetConstantValue(true);
412   } else {
413     return false;
414   }
415   return true;
416 }
417 
TraceInferFailed(ValueNode * node,int depth=0)418 static std::string TraceInferFailed(ValueNode *node, int depth = 0) {
419   std::string prefix(IntToSize(depth) << 1, ' ');
420   std::stringstream s;
421   s << prefix << node << " ";
422   switch (node->GetType()) {
423     case AbstractNode::Call:
424     case AbstractNode::Value: {
425       s << "bci " << node->bci() << " " << Opcode(node->GetOpcode()).name() << " " << node->GetOparg();
426       if (Opcode(node->GetOpcode()).HasName()) {
427         s << " " << node->GetName();
428       }
429       break;
430     }
431     case AbstractNode::Param: {
432       s << "Parameter " << node->GetOparg();
433       break;
434     }
435     case AbstractNode::CellVar:
436     case AbstractNode::FreeVar: {
437       s << "Closure " << node->GetOparg();
438       break;
439     }
440     case AbstractNode::kUnbound: {
441       s << "(kUnboundLocal)";
442       break;
443     }
444     default: {
445       break;
446     }
447   }
448   s << " object is ";
449   PyObject *op = node->GetVobj() ? node->GetVobj()->GetPyObject().ptr() : nullptr;
450   if (op != nullptr) {
451     s << AObject::ToString(op);
452     return s.str();
453   }
454   s << "<NULL>:" << std::endl;
455   for (size_t i = 0; i < node->getInputs().size(); ++i) {
456     s << prefix << " " << TraceInferFailed(node->input(i), depth + 1) << std::endl;
457   }
458   return s.str();
459 }
460 
ToString(int depth) const461 std::string Graph::ToString(int depth) const {
462   std::stringstream s;
463   std::string prefix(depth << 1, ' ');
464   std::string code_name = co_.ptr() != nullptr ? std::string(py::str(co_.ptr())) : "<no code>";
465 
466   s << prefix << "*** Trace Nodes [" << code_name << "] ***\n";
467   if (depth == 0) {
468     s << prefix << "Frame:\n" << GetFrame(0).ToString();
469   }
470 
471   s << prefix << "Nodes:\n";
472   for (auto i : GetTracedNodes()) {
473     s << prefix << i->ToString() << "\n";
474     if (i->GetType() != AbstractNode::Call) {
475       continue;
476     }
477     CallNode *node = static_cast<CallNode *>(i);
478     s << prefix << "{ inline stat " << GetInlineReasonDesc(node->GetInlineReason()) << "\n";
479     if (node->GetSubGraph() != nullptr) {
480       s << node->GetSubGraph()->ToString(depth + 1);
481     }
482     s << prefix << "}\n";
483   }
484   s << prefix << "return: " << (GetRetVal() ? GetRetVal()->ToString() : "trace break") << "\n";
485   s << prefix << GetStopTraceReasonDesc(GetStopTraceReason()) << "\n";
486   s << prefix << "break bci: " << GetStopTraceBci() << "\n\n";
487 
488   if (depth == 0) {
489     std::string break_info;
490     if (GetRetVal()) {
491       PyObject *op = GetRetVal()->GetVobj() ? GetRetVal()->GetVobj()->GetPyObject().ptr() : nullptr;
492       if (op == nullptr) {
493         break_info = TraceInferFailed(GetRetVal());
494       }
495     } else {
496       break_info = this->DumpBreakInfo();
497     }
498     if (!break_info.empty()) {
499       s << prefix << std::regex_replace(break_info, std::regex("\n"), "\n" + prefix) << "\n";
500     }
501   }
502   return s.str();
503 }
504 
DumpUnsupportedByteCodeInfo(std::stringstream & s,Opcode op,int arg)505 void DumpUnsupportedByteCodeInfo(std::stringstream &s, Opcode op, int arg) {
506   if (op == SETUP_WITH || op == SETUP_FINALLY) {
507     s << op.name() << " " << arg << " is skipped in break_graph or a exception happened.\n";
508   } else {
509     s << op.name() << " " << arg << " is not support.\n";
510   }
511 }
512 
DumpBreakInfo() const513 std::string Graph::DumpBreakInfo() const {
514   if (GetStopTraceBci() == -1) {
515     return std::string();
516   }
517   std::stringstream s;
518   const auto &f = GetFrame(GetStopTraceBci());
519   const auto &nodes = GetTracedNodes();
520   const auto &instrs = cfg_->instr_pool();
521   int break_bci = GetStopTraceBci();
522 
523   s << "graph break at: " << break_bci << ":\n";
524   std::vector<ValueNode *> parameters;
525   if (nodes.size() == 0 || nodes.back()->bci() < break_bci) {
526     // break at unsupported bytecode
527     Opcode op(instrs[break_bci]->op());
528     int arg = instrs[break_bci]->arg();
529     DumpUnsupportedByteCodeInfo(s, op, arg);
530     if (op == POP_JUMP_IF_FALSE || op == POP_JUMP_IF_TRUE || op == JUMP_IF_FALSE_OR_POP || op == JUMP_IF_TRUE_OR_POP ||
531         op == FOR_ITER || op == UNPACK_SEQUENCE || op == UNPACK_EX) {
532       parameters.push_back(f.Peek(0));
533     } else if (op == CALL_FUNCTION_EX) {
534       arg = (arg & 0x01) + 1;
535     } else if (op == CALL_FUNCTION_KW) {
536       arg++;
537     } else {
538       return s.str();
539     }
540     if (op.IsCall()) {
541       for (int i = arg; i >= 0; --i) {
542         parameters.push_back(f.Peek(i));
543         AObject *v = f.Peek(i)->GetVobj();
544         // just print the first infer failed value
545         if (v == nullptr || v->GetPyObject().ptr() == nullptr) {
546           parameters = {f.Peek(i)};
547           break;
548         }
549       }
550     }
551   } else {
552     auto iter = std::find_if(nodes.begin(), nodes.end(), [break_bci](ValueNode *n) { return n->bci() == break_bci; });
553     MS_EXCEPTION_IF_CHECK_FAIL(iter != nodes.end(), "can't find break info.");
554     parameters.push_back(*iter);
555   }
556   // print traced value
557   for (auto node : parameters) {
558     s << TraceInferFailed(node) << "\n";
559   }
560   return s.str();
561 }
562 
ToString() const563 std::string FrameStates::ToString() const {
564   std::stringstream s;
565   s << "locals:\n";
566   for (size_t i = 0; i < locals.size(); ++i) {
567     if (locals[i] != &ValueNode::kUnboundLocal) {
568       s << i << ": " << locals[i]->ToString() << "\n";
569     }
570   }
571   s << "\nstacks:\n";
572   std::for_each(stack.rbegin(), stack.rend(), [&s](ValueNode *i) { s << i->ToString() << "\n"; });
573   s << "\ncell free:\n";
574   std::for_each(cell_free.begin(), cell_free.end(), [&s](ValueNode *i) { s << i->ToString() << "\n"; });
575   s << "\n";
576   return s.str();
577 }
578 
579 }  // namespace pijit
580 }  // namespace mindspore
581