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