1// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/compiler/backend/il_printer.h"
6
7#include "vm/compiler/api/print_filter.h"
8#include "vm/compiler/backend/il.h"
9#include "vm/compiler/backend/linearscan.h"
10#include "vm/compiler/backend/range_analysis.h"
11#include "vm/compiler/ffi/native_calling_convention.h"
12#include "vm/os.h"
13#include "vm/parser.h"
14
15namespace dart {
16
17#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
18
19DEFINE_FLAG(bool,
20 display_sorted_ic_data,
21 false,
22 "Calls display a unary, sorted-by count form of ICData");
23DEFINE_FLAG(bool, print_environments, false, "Print SSA environments.");
24
25DECLARE_FLAG(bool, trace_inlining_intervals);
26
27bool FlowGraphPrinter::ShouldPrint(const Function& function) {
28 return compiler::PrintFilter::ShouldPrint(function);
29}
30
31void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
32 LogBlock lb;
33 THR_Print("*** BEGIN CFG\n%s\n", phase);
34 FlowGraphPrinter printer(*flow_graph);
35 printer.PrintBlocks();
36 THR_Print("*** END CFG\n");
37 fflush(stdout);
38}
39
40void FlowGraphPrinter::PrintBlock(BlockEntryInstr* block,
41 bool print_locations) {
42 // Print the block entry.
43 PrintOneInstruction(block, print_locations);
44 THR_Print("\n");
45 // And all the successors in the block.
46 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
47 Instruction* current = it.Current();
48 PrintOneInstruction(current, print_locations);
49 THR_Print("\n");
50 }
51}
52
53void FlowGraphPrinter::PrintBlocks() {
54 if (!function_.IsNull()) {
55 THR_Print("==== %s (%s", function_.ToFullyQualifiedCString(),
56 Function::KindToCString(function_.kind()));
57 // Output saved arguments descriptor information for dispatchers that
58 // have it, so it's easy to see which dispatcher this graph represents.
59 if (function_.IsInvokeFieldDispatcher() ||
60 function_.IsNoSuchMethodDispatcher()) {
61 const auto& args_desc_array = Array::Handle(function_.saved_args_desc());
62 const ArgumentsDescriptor args_desc(args_desc_array);
63 THR_Print(", %s", args_desc.ToCString());
64 }
65 THR_Print(")\n");
66 }
67
68 for (intptr_t i = 0; i < block_order_.length(); ++i) {
69 PrintBlock(block_order_[i], print_locations_);
70 }
71}
72
73void FlowGraphPrinter::PrintInstruction(Instruction* instr) {
74 PrintOneInstruction(instr, print_locations_);
75}
76
77void FlowGraphPrinter::PrintOneInstruction(Instruction* instr,
78 bool print_locations) {
79 char str[4000];
80 BufferFormatter f(str, sizeof(str));
81 instr->PrintTo(&f);
82 if (FLAG_print_environments && (instr->env() != NULL)) {
83 instr->env()->PrintTo(&f);
84 }
85 if (print_locations && (instr->HasLocs())) {
86 instr->locs()->PrintTo(&f);
87 }
88 if (FlowGraphAllocator::HasLifetimePosition(instr)) {
89 THR_Print("%3" Pd ": ", FlowGraphAllocator::GetLifetimePosition(instr));
90 }
91 if (!instr->IsBlockEntry()) THR_Print(" ");
92 THR_Print("%s", str);
93 if (FLAG_trace_inlining_intervals) {
94 THR_Print(" iid: %" Pd "", instr->inlining_id());
95 }
96}
97
98void FlowGraphPrinter::PrintTypeCheck(const ParsedFunction& parsed_function,
99 TokenPosition token_pos,
100 Value* value,
101 const AbstractType& dst_type,
102 const String& dst_name,
103 bool eliminated) {
104 const char* compile_type_name = "unknown";
105 if (value != NULL && value->reaching_type_ != NULL) {
106 compile_type_name = value->reaching_type_->ToCString();
107 }
108 THR_Print(
109 "%s type check: compile type %s is %s specific than "
110 "type '%s' of '%s'.\n",
111 eliminated ? "Eliminated" : "Generated", compile_type_name,
112 eliminated ? "more" : "not more",
113 String::Handle(dst_type.Name()).ToCString(), dst_name.ToCString());
114}
115
116static void PrintTargetsHelper(BaseTextBuffer* f,
117 const CallTargets& targets,
118 intptr_t num_checks_to_print) {
119 f->AddString(" Targets[");
120 f->Printf("%" Pd ": ", targets.length());
121 Function& target = Function::Handle();
122 if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) ||
123 (num_checks_to_print > targets.length())) {
124 num_checks_to_print = targets.length();
125 }
126 for (intptr_t i = 0; i < num_checks_to_print; i++) {
127 const CidRange& range = targets[i];
128 const auto target_info = targets.TargetAt(i);
129 const intptr_t count = target_info->count;
130 target = target_info->target->raw();
131 if (i > 0) {
132 f->AddString(" | ");
133 }
134 if (range.IsSingleCid()) {
135 const Class& cls =
136 Class::Handle(Isolate::Current()->class_table()->At(range.cid_start));
137 f->Printf("%s", String::Handle(cls.Name()).ToCString());
138 f->Printf(" cid %" Pd " cnt:%" Pd " trgt:'%s'", range.cid_start, count,
139 target.ToQualifiedCString());
140 } else {
141 const Class& cls = Class::Handle(target.Owner());
142 f->Printf("cid %" Pd "-%" Pd " %s", range.cid_start, range.cid_end,
143 String::Handle(cls.Name()).ToCString());
144 f->Printf(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString());
145 }
146
147 if (target_info->exactness.IsTracking()) {
148 f->Printf(" %s", target_info->exactness.ToCString());
149 }
150 }
151 if (num_checks_to_print < targets.length()) {
152 f->AddString("...");
153 }
154 f->AddString("]");
155}
156
157static void PrintCidsHelper(BaseTextBuffer* f,
158 const Cids& targets,
159 intptr_t num_checks_to_print) {
160 f->AddString(" Cids[");
161 f->Printf("%" Pd ": ", targets.length());
162 if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) ||
163 (num_checks_to_print > targets.length())) {
164 num_checks_to_print = targets.length();
165 }
166 for (intptr_t i = 0; i < num_checks_to_print; i++) {
167 const CidRange& range = targets[i];
168 if (i > 0) {
169 f->AddString(" | ");
170 }
171 const Class& cls =
172 Class::Handle(Isolate::Current()->class_table()->At(range.cid_start));
173 f->Printf("%s etc. ", String::Handle(cls.Name()).ToCString());
174 if (range.IsSingleCid()) {
175 f->Printf(" cid %" Pd, range.cid_start);
176 } else {
177 f->Printf(" cid %" Pd "-%" Pd, range.cid_start, range.cid_end);
178 }
179 }
180 if (num_checks_to_print < targets.length()) {
181 f->AddString("...");
182 }
183 f->AddString("]");
184}
185
186static void PrintICDataHelper(BaseTextBuffer* f,
187 const ICData& ic_data,
188 intptr_t num_checks_to_print) {
189 f->AddString(" IC[");
190 if (ic_data.is_tracking_exactness()) {
191 f->Printf(
192 "(%s) ",
193 AbstractType::Handle(ic_data.receivers_static_type()).ToCString());
194 }
195 f->Printf("%" Pd ": ", ic_data.NumberOfChecks());
196 Function& target = Function::Handle();
197 if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) ||
198 (num_checks_to_print > ic_data.NumberOfChecks())) {
199 num_checks_to_print = ic_data.NumberOfChecks();
200 }
201 for (intptr_t i = 0; i < num_checks_to_print; i++) {
202 GrowableArray<intptr_t> class_ids;
203 ic_data.GetCheckAt(i, &class_ids, &target);
204 const intptr_t count = ic_data.GetCountAt(i);
205 if (i > 0) {
206 f->AddString(" | ");
207 }
208 for (intptr_t k = 0; k < class_ids.length(); k++) {
209 if (k > 0) {
210 f->AddString(", ");
211 }
212 const Class& cls =
213 Class::Handle(Isolate::Current()->class_table()->At(class_ids[k]));
214 f->Printf("%s", String::Handle(cls.Name()).ToCString());
215 }
216 f->Printf(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString());
217 if (ic_data.is_tracking_exactness()) {
218 f->Printf(" %s", ic_data.GetExactnessAt(i).ToCString());
219 }
220 }
221 if (num_checks_to_print < ic_data.NumberOfChecks()) {
222 f->AddString("...");
223 }
224 f->AddString("]");
225}
226
227static void PrintICDataSortedHelper(BaseTextBuffer* f,
228 const ICData& ic_data_orig) {
229 const ICData& ic_data =
230 ICData::Handle(ic_data_orig.AsUnaryClassChecksSortedByCount());
231 f->Printf(" IC[n:%" Pd "; ", ic_data.NumberOfChecks());
232 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
233 const intptr_t count = ic_data.GetCountAt(i);
234 const intptr_t cid = ic_data.GetReceiverClassIdAt(i);
235 const Class& cls =
236 Class::Handle(Isolate::Current()->class_table()->At(cid));
237 f->Printf("%s : %" Pd ", ", String::Handle(cls.Name()).ToCString(), count);
238 }
239 f->AddString("]");
240}
241
242void FlowGraphPrinter::PrintICData(const ICData& ic_data,
243 intptr_t num_checks_to_print) {
244 char buffer[1024];
245 BufferFormatter f(buffer, sizeof(buffer));
246 PrintICDataHelper(&f, ic_data, num_checks_to_print);
247 THR_Print("%s ", buffer);
248 const Array& a = Array::Handle(ic_data.arguments_descriptor());
249 THR_Print(" arg-desc %" Pd "\n", a.Length());
250}
251
252void FlowGraphPrinter::PrintCidRangeData(const CallTargets& targets,
253 intptr_t num_checks_to_print) {
254 char buffer[1024];
255 BufferFormatter f(buffer, sizeof(buffer));
256 PrintTargetsHelper(&f, targets, num_checks_to_print);
257 THR_Print("%s ", buffer);
258 // TODO(erikcorry): Print args descriptor.
259}
260
261static void PrintUse(BaseTextBuffer* f, const Definition& definition) {
262 if (definition.HasSSATemp()) {
263 if (definition.HasPairRepresentation()) {
264 f->Printf("(v%" Pd ", v%" Pd ")", definition.ssa_temp_index(),
265 definition.ssa_temp_index() + 1);
266 } else {
267 f->Printf("v%" Pd "", definition.ssa_temp_index());
268 }
269 } else if (definition.HasTemp()) {
270 f->Printf("t%" Pd "", definition.temp_index());
271 }
272}
273
274const char* Instruction::ToCString() const {
275 char buffer[1024];
276 BufferFormatter f(buffer, sizeof(buffer));
277 PrintTo(&f);
278 return Thread::Current()->zone()->MakeCopyOfString(buffer);
279}
280
281void Instruction::PrintTo(BaseTextBuffer* f) const {
282 if (GetDeoptId() != DeoptId::kNone) {
283 f->Printf("%s:%" Pd "(", DebugName(), GetDeoptId());
284 } else {
285 f->Printf("%s(", DebugName());
286 }
287 PrintOperandsTo(f);
288 f->AddString(")");
289}
290
291void Instruction::PrintOperandsTo(BaseTextBuffer* f) const {
292 for (int i = 0; i < InputCount(); ++i) {
293 if (i > 0) f->AddString(", ");
294 if (InputAt(i) != NULL) InputAt(i)->PrintTo(f);
295 }
296}
297
298void Definition::PrintTo(BaseTextBuffer* f) const {
299 PrintUse(f, *this);
300 if (HasSSATemp() || HasTemp()) f->AddString(" <- ");
301 if (GetDeoptId() != DeoptId::kNone) {
302 f->Printf("%s:%" Pd "(", DebugName(), GetDeoptId());
303 } else {
304 f->Printf("%s(", DebugName());
305 }
306 PrintOperandsTo(f);
307 f->AddString(")");
308 if (range_ != NULL) {
309 f->AddString(" ");
310 range_->PrintTo(f);
311 }
312
313 if (type_ != NULL) {
314 f->AddString(" ");
315 type_->PrintTo(f);
316 }
317}
318
319void CheckNullInstr::PrintOperandsTo(BaseTextBuffer* f) const {
320 Definition::PrintOperandsTo(f);
321 switch (exception_type()) {
322 case kNoSuchMethod:
323 f->AddString(", NoSuchMethodError");
324 break;
325 case kArgumentError:
326 f->AddString(", ArgumentError");
327 break;
328 case kCastError:
329 f->AddString(", CastError");
330 break;
331 }
332}
333
334void Definition::PrintOperandsTo(BaseTextBuffer* f) const {
335 for (int i = 0; i < InputCount(); ++i) {
336 if (i > 0) f->AddString(", ");
337 if (InputAt(i) != NULL) {
338 InputAt(i)->PrintTo(f);
339 }
340 }
341}
342
343void RedefinitionInstr::PrintOperandsTo(BaseTextBuffer* f) const {
344 Definition::PrintOperandsTo(f);
345 if (constrained_type_ != nullptr) {
346 f->Printf(" ^ %s", constrained_type_->ToCString());
347 }
348}
349
350void ReachabilityFenceInstr::PrintOperandsTo(BaseTextBuffer* f) const {
351 value()->PrintTo(f);
352}
353
354void Value::PrintTo(BaseTextBuffer* f) const {
355 PrintUse(f, *definition());
356
357 if ((reaching_type_ != NULL) && (reaching_type_ != definition()->type_)) {
358 f->AddString(" ");
359 reaching_type_->PrintTo(f);
360 }
361}
362
363void ConstantInstr::PrintOperandsTo(BaseTextBuffer* f) const {
364 const char* cstr = value().ToCString();
365 const char* new_line = strchr(cstr, '\n');
366 if (new_line == NULL) {
367 f->Printf("#%s", cstr);
368 } else {
369 const intptr_t pos = new_line - cstr;
370 char* buffer = Thread::Current()->zone()->Alloc<char>(pos + 1);
371 strncpy(buffer, cstr, pos);
372 buffer[pos] = '\0';
373 f->Printf("#%s\\n...", buffer);
374 }
375}
376
377void ConstraintInstr::PrintOperandsTo(BaseTextBuffer* f) const {
378 value()->PrintTo(f);
379 f->AddString(" ^ ");
380 constraint()->PrintTo(f);
381}
382
383void Range::PrintTo(BaseTextBuffer* f) const {
384 f->AddString("[");
385 min_.PrintTo(f);
386 f->AddString(", ");
387 max_.PrintTo(f);
388 f->AddString("]");
389}
390
391const char* Range::ToCString(const Range* range) {
392 if (range == NULL) return "[_|_, _|_]";
393
394 char buffer[256];
395 BufferFormatter f(buffer, sizeof(buffer));
396 range->PrintTo(&f);
397 return Thread::Current()->zone()->MakeCopyOfString(buffer);
398}
399
400void RangeBoundary::PrintTo(BaseTextBuffer* f) const {
401 switch (kind_) {
402 case kSymbol:
403 f->Printf("v%" Pd "",
404 reinterpret_cast<Definition*>(value_)->ssa_temp_index());
405 if (offset_ != 0) f->Printf("%+" Pd64 "", offset_);
406 break;
407 case kNegativeInfinity:
408 f->AddString("-inf");
409 break;
410 case kPositiveInfinity:
411 f->AddString("+inf");
412 break;
413 case kConstant:
414 f->Printf("%" Pd64 "", value_);
415 break;
416 case kUnknown:
417 f->AddString("_|_");
418 break;
419 }
420}
421
422const char* RangeBoundary::ToCString() const {
423 char buffer[256];
424 BufferFormatter f(buffer, sizeof(buffer));
425 PrintTo(&f);
426 return Thread::Current()->zone()->MakeCopyOfString(buffer);
427}
428
429void MakeTempInstr::PrintOperandsTo(BaseTextBuffer* f) const {}
430
431void DropTempsInstr::PrintOperandsTo(BaseTextBuffer* f) const {
432 f->Printf("%" Pd "", num_temps());
433 if (value() != NULL) {
434 f->AddString(", ");
435 value()->PrintTo(f);
436 }
437}
438
439void AssertAssignableInstr::PrintOperandsTo(BaseTextBuffer* f) const {
440 value()->PrintTo(f);
441 f->AddString(", ");
442 dst_type()->PrintTo(f);
443 f->Printf(", '%s',", dst_name().ToCString());
444 f->AddString(" instantiator_type_args(");
445 instantiator_type_arguments()->PrintTo(f);
446 f->AddString("), function_type_args(");
447 function_type_arguments()->PrintTo(f);
448 f->AddString(")");
449}
450
451void AssertSubtypeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
452 sub_type()->PrintTo(f);
453 f->AddString(", ");
454 super_type()->PrintTo(f);
455 f->Printf(", '%s', ", dst_name().ToCString());
456 f->AddString(" instantiator_type_args(");
457 instantiator_type_arguments()->PrintTo(f);
458 f->AddString("), function_type_args(");
459 function_type_arguments()->PrintTo(f);
460 f->AddString(")");
461}
462
463void AssertBooleanInstr::PrintOperandsTo(BaseTextBuffer* f) const {
464 value()->PrintTo(f);
465}
466
467void ClosureCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
468 f->AddString(" function=");
469 InputAt(InputCount() - 1)->PrintTo(f);
470 f->Printf("<%" Pd ">", type_args_len());
471 for (intptr_t i = 0; i < ArgumentCount(); ++i) {
472 f->AddString(", ");
473 ArgumentValueAt(i)->PrintTo(f);
474 }
475 if (entry_kind() == Code::EntryKind::kUnchecked) {
476 f->AddString(" using unchecked entrypoint");
477 }
478}
479
480void InstanceCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
481 f->Printf(" %s<%" Pd ">", function_name().ToCString(), type_args_len());
482 for (intptr_t i = 0; i < ArgumentCount(); ++i) {
483 f->AddString(", ");
484 ArgumentValueAt(i)->PrintTo(f);
485 }
486 if (HasICData()) {
487 if (FLAG_display_sorted_ic_data) {
488 PrintICDataSortedHelper(f, *ic_data());
489 } else {
490 PrintICDataHelper(f, *ic_data(), FlowGraphPrinter::kPrintAll);
491 }
492 }
493 if (result_type() != nullptr) {
494 f->Printf(", result_type = %s", result_type()->ToCString());
495 }
496 if (entry_kind() == Code::EntryKind::kUnchecked) {
497 f->AddString(" using unchecked entrypoint");
498 }
499}
500
501void PolymorphicInstanceCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
502 f->Printf(" %s<%" Pd ">", function_name().ToCString(), type_args_len());
503 for (intptr_t i = 0; i < ArgumentCount(); ++i) {
504 f->AddString(", ");
505 ArgumentValueAt(i)->PrintTo(f);
506 }
507 PrintTargetsHelper(f, targets_, FlowGraphPrinter::kPrintAll);
508 if (complete()) {
509 f->AddString(" COMPLETE");
510 }
511 if (entry_kind() == Code::EntryKind::kUnchecked) {
512 f->AddString(" using unchecked entrypoint");
513 }
514}
515
516void DispatchTableCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
517 const String& name =
518 String::Handle(interface_target().QualifiedUserVisibleName());
519 f->AddString(" cid=");
520 class_id()->PrintTo(f);
521 f->Printf(" %s<%" Pd ">", name.ToCString(), type_args_len());
522 for (intptr_t i = 0; i < ArgumentCount(); ++i) {
523 f->AddString(", ");
524 ArgumentValueAt(i)->PrintTo(f);
525 }
526}
527
528void StrictCompareInstr::PrintOperandsTo(BaseTextBuffer* f) const {
529 f->Printf("%s, ", Token::Str(kind()));
530 left()->PrintTo(f);
531 f->AddString(", ");
532 right()->PrintTo(f);
533 if (needs_number_check()) {
534 f->Printf(", with number check");
535 }
536}
537
538void TestCidsInstr::PrintOperandsTo(BaseTextBuffer* f) const {
539 left()->PrintTo(f);
540 f->Printf(" %s [", Token::Str(kind()));
541 intptr_t length = cid_results().length();
542 for (intptr_t i = 0; i < length; i += 2) {
543 f->Printf("0x%" Px ":%s ", cid_results()[i],
544 cid_results()[i + 1] == 0 ? "false" : "true");
545 }
546 f->AddString("] ");
547 if (CanDeoptimize()) {
548 ASSERT(deopt_id() != DeoptId::kNone);
549 f->AddString("else deoptimize ");
550 } else {
551 ASSERT(deopt_id() == DeoptId::kNone);
552 f->Printf("else %s ", cid_results()[length - 1] != 0 ? "false" : "true");
553 }
554}
555
556void EqualityCompareInstr::PrintOperandsTo(BaseTextBuffer* f) const {
557 left()->PrintTo(f);
558 f->Printf(" %s ", Token::Str(kind()));
559 right()->PrintTo(f);
560}
561
562void StaticCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
563 f->Printf(" %s<%" Pd "> ", String::Handle(function().name()).ToCString(),
564 type_args_len());
565 for (intptr_t i = 0; i < ArgumentCount(); ++i) {
566 if (i > 0) f->AddString(", ");
567 ArgumentValueAt(i)->PrintTo(f);
568 }
569 if (entry_kind() == Code::EntryKind::kUnchecked) {
570 f->AddString(", using unchecked entrypoint");
571 }
572 if (function().recognized_kind() != MethodRecognizer::kUnknown) {
573 f->Printf(", recognized_kind = %s",
574 MethodRecognizer::KindToCString(function().recognized_kind()));
575 }
576 if (result_type() != nullptr) {
577 f->Printf(", result_type = %s", result_type()->ToCString());
578 }
579}
580
581void LoadLocalInstr::PrintOperandsTo(BaseTextBuffer* f) const {
582 f->Printf("%s @%d", local().name().ToCString(), local().index().value());
583}
584
585void StoreLocalInstr::PrintOperandsTo(BaseTextBuffer* f) const {
586 f->Printf("%s @%d, ", local().name().ToCString(), local().index().value());
587 value()->PrintTo(f);
588}
589
590void NativeCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
591 f->Printf("%s", native_name().ToCString());
592}
593
594void GuardFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const {
595 f->Printf("%s %s, ", String::Handle(field().name()).ToCString(),
596 field().GuardedPropertiesAsCString());
597 value()->PrintTo(f);
598}
599
600void StoreInstanceFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const {
601 instance()->PrintTo(f);
602 f->Printf(" . %s = ", slot().Name());
603 value()->PrintTo(f);
604
605 // Here, we just print the value of the enum field. We would prefer to get
606 // the final decision on whether a store barrier will be emitted by calling
607 // ShouldEmitStoreBarrier(), but that can change parts of the flow graph.
608 if (emit_store_barrier_ == kNoStoreBarrier) {
609 f->AddString(", NoStoreBarrier");
610 }
611}
612
613void IfThenElseInstr::PrintOperandsTo(BaseTextBuffer* f) const {
614 comparison()->PrintOperandsTo(f);
615 f->Printf(" ? %" Pd " : %" Pd, if_true_, if_false_);
616}
617
618void LoadStaticFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const {
619 f->Printf("%s", String::Handle(field().name()).ToCString());
620 if (calls_initializer()) {
621 f->AddString(", CallsInitializer");
622 }
623}
624
625void StoreStaticFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const {
626 f->Printf("%s, ", String::Handle(field().name()).ToCString());
627 value()->PrintTo(f);
628}
629
630void InstanceOfInstr::PrintOperandsTo(BaseTextBuffer* f) const {
631 value()->PrintTo(f);
632 f->Printf(" IS %s,", String::Handle(type().Name()).ToCString());
633 f->AddString(" instantiator_type_args(");
634 instantiator_type_arguments()->PrintTo(f);
635 f->AddString("), function_type_args(");
636 function_type_arguments()->PrintTo(f);
637 f->AddString(")");
638}
639
640void RelationalOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
641 f->Printf("%s, ", Token::Str(kind()));
642 left()->PrintTo(f);
643 f->AddString(", ");
644 right()->PrintTo(f);
645}
646
647void AllocateObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const {
648 f->Printf("%s", String::Handle(cls().ScrubbedName()).ToCString());
649 for (intptr_t i = 0; i < InputCount(); ++i) {
650 f->AddString(", ");
651 InputAt(i)->PrintTo(f);
652 }
653 if (Identity().IsNotAliased()) {
654 f->AddString(" <not-aliased>");
655 }
656}
657
658void MaterializeObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const {
659 f->Printf("%s", String::Handle(cls_.ScrubbedName()).ToCString());
660 for (intptr_t i = 0; i < InputCount(); i++) {
661 f->AddString(", ");
662 f->Printf("%s: ", slots_[i]->Name());
663 InputAt(i)->PrintTo(f);
664 }
665}
666
667void LoadFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const {
668 instance()->PrintTo(f);
669 f->Printf(" . %s%s", slot().Name(), slot().is_immutable() ? " {final}" : "");
670 if (calls_initializer()) {
671 f->AddString(", CallsInitializer");
672 }
673}
674
675void LoadUntaggedInstr::PrintOperandsTo(BaseTextBuffer* f) const {
676 object()->PrintTo(f);
677 f->Printf(", %" Pd, offset());
678}
679
680void InstantiateTypeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
681 const String& type_name = String::Handle(type().Name());
682 f->Printf("%s,", type_name.ToCString());
683 f->AddString(" instantiator_type_args(");
684 instantiator_type_arguments()->PrintTo(f);
685 f->AddString("), function_type_args(");
686 function_type_arguments()->PrintTo(f);
687 f->AddString(")");
688}
689
690void InstantiateTypeArgumentsInstr::PrintOperandsTo(BaseTextBuffer* f) const {
691 const String& type_args = String::Handle(type_arguments().Name());
692 f->Printf("%s,", type_args.ToCString());
693 f->AddString(" instantiator_type_args(");
694 instantiator_type_arguments()->PrintTo(f);
695 f->AddString("), function_type_args(");
696 function_type_arguments()->PrintTo(f);
697 f->Printf("), instantiator_class(%s)", instantiator_class().ToCString());
698}
699
700void AllocateContextInstr::PrintOperandsTo(BaseTextBuffer* f) const {
701 f->Printf("%" Pd "", num_context_variables());
702}
703
704void AllocateUninitializedContextInstr::PrintOperandsTo(
705 BaseTextBuffer* f) const {
706 f->Printf("%" Pd "", num_context_variables());
707
708 if (Identity().IsNotAliased()) {
709 f->AddString(" <not-aliased>");
710 }
711}
712
713void MathUnaryInstr::PrintOperandsTo(BaseTextBuffer* f) const {
714 f->Printf("'%s', ", MathUnaryInstr::KindToCString(kind()));
715 value()->PrintTo(f);
716}
717
718void TruncDivModInstr::PrintOperandsTo(BaseTextBuffer* f) const {
719 Definition::PrintOperandsTo(f);
720}
721
722void ExtractNthOutputInstr::PrintOperandsTo(BaseTextBuffer* f) const {
723 f->Printf("Extract %" Pd " from ", index());
724 Definition::PrintOperandsTo(f);
725}
726
727void UnaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
728 f->Printf("%s, ", Token::Str(op_kind()));
729 value()->PrintTo(f);
730}
731
732void CheckedSmiOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
733 f->Printf("%s", Token::Str(op_kind()));
734 f->AddString(", ");
735 left()->PrintTo(f);
736 f->AddString(", ");
737 right()->PrintTo(f);
738}
739
740void CheckedSmiComparisonInstr::PrintOperandsTo(BaseTextBuffer* f) const {
741 f->Printf("%s", Token::Str(kind()));
742 f->AddString(", ");
743 left()->PrintTo(f);
744 f->AddString(", ");
745 right()->PrintTo(f);
746}
747
748void BinaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
749 f->Printf("%s", Token::Str(op_kind()));
750 if (is_truncating()) {
751 f->AddString(" [tr]");
752 } else if (!can_overflow()) {
753 f->AddString(" [-o]");
754 }
755 f->AddString(", ");
756 left()->PrintTo(f);
757 f->AddString(", ");
758 right()->PrintTo(f);
759}
760
761void BinaryDoubleOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
762 f->Printf("%s, ", Token::Str(op_kind()));
763 left()->PrintTo(f);
764 f->AddString(", ");
765 right()->PrintTo(f);
766}
767
768void DoubleTestOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
769 switch (op_kind()) {
770 case MethodRecognizer::kDouble_getIsNaN:
771 f->AddString("IsNaN ");
772 break;
773 case MethodRecognizer::kDouble_getIsInfinite:
774 f->AddString("IsInfinite ");
775 break;
776 default:
777 UNREACHABLE();
778 }
779 value()->PrintTo(f);
780}
781
782static const char* simd_op_kind_string[] = {
783#define CASE(Arity, Mask, Name, ...) #Name,
784 SIMD_OP_LIST(CASE, CASE)
785#undef CASE
786};
787
788void SimdOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
789 f->Printf("%s", simd_op_kind_string[kind()]);
790 if (HasMask()) {
791 f->Printf(", mask = %" Pd "", mask());
792 }
793 for (intptr_t i = 0; i < InputCount(); i++) {
794 f->AddString(", ");
795 InputAt(i)->PrintTo(f);
796 }
797}
798
799void UnaryDoubleOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
800 f->Printf("%s, ", Token::Str(op_kind()));
801 value()->PrintTo(f);
802}
803
804void LoadClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const {
805 if (!input_can_be_smi_) {
806 f->AddString("<non-smi> ");
807 }
808 object()->PrintTo(f);
809}
810
811void CheckClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const {
812 value()->PrintTo(f);
813
814 const Class& cls =
815 Class::Handle(Isolate::Current()->class_table()->At(cids().cid_start));
816 const String& name = String::Handle(cls.ScrubbedName());
817 if (cids().IsSingleCid()) {
818 f->Printf(", %s", name.ToCString());
819 } else {
820 const Class& cls2 =
821 Class::Handle(Isolate::Current()->class_table()->At(cids().cid_end));
822 const String& name2 = String::Handle(cls2.ScrubbedName());
823 f->Printf(", cid %" Pd "-%" Pd " %s-%s", cids().cid_start, cids().cid_end,
824 name.ToCString(), name2.ToCString());
825 }
826}
827
828void CheckClassInstr::PrintOperandsTo(BaseTextBuffer* f) const {
829 value()->PrintTo(f);
830 PrintCidsHelper(f, cids_, FlowGraphPrinter::kPrintAll);
831 if (IsNullCheck()) {
832 f->AddString(" nullcheck");
833 }
834}
835
836void CheckConditionInstr::PrintOperandsTo(BaseTextBuffer* f) const {
837 comparison()->PrintOperandsTo(f);
838}
839
840void InvokeMathCFunctionInstr::PrintOperandsTo(BaseTextBuffer* f) const {
841 f->Printf("%s, ", MethodRecognizer::KindToCString(recognized_kind_));
842 Definition::PrintOperandsTo(f);
843}
844
845void BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(
846 BaseTextBuffer* f) const {
847 const GrowableArray<Definition*>& defns = initial_definitions_;
848 if (defns.length() > 0) {
849 f->AddString(" {");
850 for (intptr_t i = 0; i < defns.length(); ++i) {
851 Definition* def = defns[i];
852 f->AddString("\n ");
853 def->PrintTo(f);
854 }
855 f->AddString("\n}");
856 }
857}
858
859void GraphEntryInstr::PrintTo(BaseTextBuffer* f) const {
860 f->Printf("B%" Pd "[graph]:%" Pd, block_id(), GetDeoptId());
861 BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
862}
863
864void JoinEntryInstr::PrintTo(BaseTextBuffer* f) const {
865 if (try_index() != kInvalidTryIndex) {
866 f->Printf("B%" Pd "[join try_idx %" Pd "]:%" Pd " pred(", block_id(),
867 try_index(), GetDeoptId());
868 } else {
869 f->Printf("B%" Pd "[join]:%" Pd " pred(", block_id(), GetDeoptId());
870 }
871 for (intptr_t i = 0; i < predecessors_.length(); ++i) {
872 if (i > 0) f->AddString(", ");
873 f->Printf("B%" Pd, predecessors_[i]->block_id());
874 }
875 f->AddString(")");
876 if (phis_ != NULL) {
877 f->AddString(" {");
878 for (intptr_t i = 0; i < phis_->length(); ++i) {
879 if ((*phis_)[i] == NULL) continue;
880 f->AddString("\n ");
881 (*phis_)[i]->PrintTo(f);
882 }
883 f->AddString("\n}");
884 }
885 if (HasParallelMove()) {
886 f->AddString(" ");
887 parallel_move()->PrintTo(f);
888 }
889}
890
891void IndirectEntryInstr::PrintTo(BaseTextBuffer* f) const {
892 f->Printf("B%" Pd "[join indirect", block_id());
893 if (try_index() != kInvalidTryIndex) {
894 f->Printf(" try_idx %" Pd, try_index());
895 }
896 f->Printf("]:%" Pd " pred(", GetDeoptId());
897 for (intptr_t i = 0; i < predecessors_.length(); ++i) {
898 if (i > 0) f->AddString(", ");
899 f->Printf("B%" Pd, predecessors_[i]->block_id());
900 }
901 f->AddString(")");
902 if (phis_ != NULL) {
903 f->AddString(" {");
904 for (intptr_t i = 0; i < phis_->length(); ++i) {
905 if ((*phis_)[i] == NULL) continue;
906 f->AddString("\n ");
907 (*phis_)[i]->PrintTo(f);
908 }
909 f->AddString("\n}");
910 }
911 if (HasParallelMove()) {
912 f->AddString(" ");
913 parallel_move()->PrintTo(f);
914 }
915}
916
917const char* RepresentationToCString(Representation rep) {
918 switch (rep) {
919 case kTagged:
920 return "tagged";
921 case kUntagged:
922 return "untagged";
923 case kUnboxedDouble:
924 return "double";
925 case kUnboxedFloat:
926 return "float";
927 case kUnboxedInt32:
928 return "int32";
929 case kUnboxedUint32:
930 return "uint32";
931 case kUnboxedInt64:
932 return "int64";
933 case kUnboxedFloat32x4:
934 return "float32x4";
935 case kUnboxedInt32x4:
936 return "int32x4";
937 case kUnboxedFloat64x2:
938 return "float64x2";
939 case kPairOfTagged:
940 return "tagged-pair";
941 case kNoRepresentation:
942 return "none";
943 case kNumRepresentations:
944 UNREACHABLE();
945 }
946 return "?";
947}
948
949void PhiInstr::PrintTo(BaseTextBuffer* f) const {
950 if (HasPairRepresentation()) {
951 f->Printf("(v%" Pd ", v%" Pd ") <- phi(", ssa_temp_index(),
952 ssa_temp_index() + 1);
953 } else {
954 f->Printf("v%" Pd " <- phi(", ssa_temp_index());
955 }
956 for (intptr_t i = 0; i < inputs_.length(); ++i) {
957 if (inputs_[i] != NULL) inputs_[i]->PrintTo(f);
958 if (i < inputs_.length() - 1) f->AddString(", ");
959 }
960 f->AddString(")");
961 f->AddString(is_alive() ? " alive" : " dead");
962 if (range_ != NULL) {
963 f->AddString(" ");
964 range_->PrintTo(f);
965 }
966
967 if (representation() != kNoRepresentation && representation() != kTagged) {
968 f->Printf(" %s", RepresentationToCString(representation()));
969 }
970
971 if (HasType()) {
972 f->Printf(" %s", TypeAsCString());
973 }
974}
975
976void UnboxIntegerInstr::PrintOperandsTo(BaseTextBuffer* f) const {
977 if (is_truncating()) {
978 f->AddString("[tr], ");
979 }
980 Definition::PrintOperandsTo(f);
981}
982
983void IntConverterInstr::PrintOperandsTo(BaseTextBuffer* f) const {
984 f->Printf("%s->%s%s, ", RepresentationToCString(from()),
985 RepresentationToCString(to()), is_truncating() ? "[tr]" : "");
986 Definition::PrintOperandsTo(f);
987}
988
989void BitCastInstr::PrintOperandsTo(BaseTextBuffer* f) const {
990 Definition::PrintOperandsTo(f);
991 f->Printf(" (%s -> %s)", RepresentationToCString(from()),
992 RepresentationToCString(to()));
993}
994
995void ParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const {
996 f->Printf("%" Pd, index());
997}
998
999void SpecialParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1000 f->Printf("%s", KindToCString(kind()));
1001}
1002
1003const char* SpecialParameterInstr::ToCString() const {
1004 char buffer[1024];
1005 BufferFormatter bf(buffer, 1024);
1006 PrintTo(&bf);
1007 return Thread::Current()->zone()->MakeCopyOfString(buffer);
1008}
1009
1010void CheckStackOverflowInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1011 f->Printf("stack=%" Pd ", loop=%" Pd, stack_depth(), loop_depth());
1012}
1013
1014void TargetEntryInstr::PrintTo(BaseTextBuffer* f) const {
1015 if (try_index() != kInvalidTryIndex) {
1016 f->Printf("B%" Pd "[target try_idx %" Pd "]:%" Pd, block_id(), try_index(),
1017 GetDeoptId());
1018 } else {
1019 f->Printf("B%" Pd "[target]:%" Pd, block_id(), GetDeoptId());
1020 }
1021 if (HasParallelMove()) {
1022 f->AddString(" ");
1023 parallel_move()->PrintTo(f);
1024 }
1025}
1026
1027void OsrEntryInstr::PrintTo(BaseTextBuffer* f) const {
1028 f->Printf("B%" Pd "[osr entry]:%" Pd " stack_depth=%" Pd, block_id(),
1029 GetDeoptId(), stack_depth());
1030 if (HasParallelMove()) {
1031 f->AddString("\n");
1032 parallel_move()->PrintTo(f);
1033 }
1034 BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
1035}
1036
1037void FunctionEntryInstr::PrintTo(BaseTextBuffer* f) const {
1038 f->Printf("B%" Pd "[function entry]:%" Pd, block_id(), GetDeoptId());
1039 if (HasParallelMove()) {
1040 f->AddString("\n");
1041 parallel_move()->PrintTo(f);
1042 }
1043 BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
1044}
1045
1046void NativeEntryInstr::PrintTo(BaseTextBuffer* f) const {
1047 f->Printf("B%" Pd "[native function entry]:%" Pd, block_id(), GetDeoptId());
1048 if (HasParallelMove()) {
1049 f->AddString("\n");
1050 parallel_move()->PrintTo(f);
1051 }
1052 BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
1053}
1054
1055void ReturnInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1056 Instruction::PrintOperandsTo(f);
1057 if (yield_index() != PcDescriptorsLayout::kInvalidYieldIndex) {
1058 f->Printf(", yield_index = %" Pd "", yield_index());
1059 }
1060}
1061
1062void FfiCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1063 f->AddString(" pointer=");
1064 InputAt(TargetAddressIndex())->PrintTo(f);
1065 for (intptr_t i = 0, n = InputCount(); i < n - 1; ++i) {
1066 f->AddString(", ");
1067 InputAt(i)->PrintTo(f);
1068 f->AddString(" (@");
1069 marshaller_.Location(i).PrintTo(f);
1070 f->AddString(")");
1071 }
1072}
1073
1074void EnterHandleScopeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1075 if (kind_ == Kind::kEnterHandleScope) {
1076 f->AddString("<enter handle scope>");
1077 } else {
1078 f->AddString("<get top api scope>");
1079 }
1080}
1081
1082void NativeReturnInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1083 value()->PrintTo(f);
1084 f->AddString(" (@");
1085 marshaller_.Location(compiler::ffi::kResultIndex).PrintTo(f);
1086 f->AddString(")");
1087}
1088
1089void NativeParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1090 // Where the calling convention puts it.
1091 marshaller_.Location(index_).PrintTo(f);
1092 f->AddString(" at ");
1093 // Where the arguments are when pushed on the stack.
1094 marshaller_.NativeLocationOfNativeParameter(index_).PrintTo(f);
1095}
1096
1097void CatchBlockEntryInstr::PrintTo(BaseTextBuffer* f) const {
1098 f->Printf("B%" Pd "[target catch try_idx %" Pd " catch_try_idx %" Pd "]",
1099 block_id(), try_index(), catch_try_index());
1100 if (HasParallelMove()) {
1101 f->AddString("\n");
1102 parallel_move()->PrintTo(f);
1103 }
1104
1105 BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f);
1106}
1107
1108void LoadIndexedUnsafeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1109 f->Printf("%s[", RegisterNames::RegisterName(base_reg()));
1110 index()->PrintTo(f);
1111 f->Printf(" + %" Pd "]", offset());
1112}
1113
1114void StoreIndexedUnsafeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1115 f->Printf("%s[", RegisterNames::RegisterName(base_reg()));
1116 index()->PrintTo(f);
1117 f->Printf(" + %" Pd "], ", offset());
1118 value()->PrintTo(f);
1119}
1120
1121void StoreIndexedInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1122 Instruction::PrintOperandsTo(f);
1123 if (!ShouldEmitStoreBarrier()) {
1124 f->AddString(", NoStoreBarrier");
1125 }
1126}
1127
1128void TailCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1129 const char* name = "<unknown code>";
1130 if (code_.IsStubCode()) {
1131 name = StubCode::NameOfStub(code_.EntryPoint());
1132 } else {
1133 const Object& owner = Object::Handle(code_.owner());
1134 if (owner.IsFunction()) {
1135 name = Function::Handle(Function::RawCast(owner.raw()))
1136 .ToFullyQualifiedCString();
1137 }
1138 }
1139 f->Printf("%s(", name);
1140 InputAt(0)->PrintTo(f);
1141 f->AddString(")");
1142}
1143
1144void PushArgumentInstr::PrintOperandsTo(BaseTextBuffer* f) const {
1145 value()->PrintTo(f);
1146}
1147
1148void GotoInstr::PrintTo(BaseTextBuffer* f) const {
1149 if (HasParallelMove()) {
1150 parallel_move()->PrintTo(f);
1151 f->AddString(" ");
1152 }
1153 if (GetDeoptId() != DeoptId::kNone) {
1154 f->Printf("goto:%" Pd " B%" Pd "", GetDeoptId(), successor()->block_id());
1155 } else {
1156 f->Printf("goto: B%" Pd "", successor()->block_id());
1157 }
1158}
1159
1160void IndirectGotoInstr::PrintTo(BaseTextBuffer* f) const {
1161 if (GetDeoptId() != DeoptId::kNone) {
1162 f->Printf("igoto:%" Pd "(", GetDeoptId());
1163 } else {
1164 f->AddString("igoto:(");
1165 }
1166 InputAt(0)->PrintTo(f);
1167 f->AddString(")");
1168}
1169
1170void BranchInstr::PrintTo(BaseTextBuffer* f) const {
1171 f->Printf("%s ", DebugName());
1172 f->AddString("if ");
1173 comparison()->PrintTo(f);
1174
1175 f->Printf(" goto (%" Pd ", %" Pd ")", true_successor()->block_id(),
1176 false_successor()->block_id());
1177}
1178
1179void ParallelMoveInstr::PrintTo(BaseTextBuffer* f) const {
1180 f->Printf("%s ", DebugName());
1181 for (intptr_t i = 0; i < moves_.length(); i++) {
1182 if (i != 0) f->AddString(", ");
1183 moves_[i]->dest().PrintTo(f);
1184 f->AddString(" <- ");
1185 moves_[i]->src().PrintTo(f);
1186 }
1187}
1188
1189void Utf8ScanInstr::PrintTo(BaseTextBuffer* f) const {
1190 Definition::PrintTo(f);
1191 f->Printf(" [%s]", scan_flags_field_.Name());
1192}
1193
1194void Environment::PrintTo(BaseTextBuffer* f) const {
1195 f->AddString(" env={ ");
1196 int arg_count = 0;
1197 for (intptr_t i = 0; i < values_.length(); ++i) {
1198 if (i > 0) f->AddString(", ");
1199 if (values_[i]->definition()->IsPushArgument()) {
1200 f->Printf("a%d", arg_count++);
1201 } else {
1202 values_[i]->PrintTo(f);
1203 }
1204 if ((locations_ != NULL) && !locations_[i].IsInvalid()) {
1205 f->AddString(" [");
1206 locations_[i].PrintTo(f);
1207 f->AddString("]");
1208 }
1209 }
1210 f->AddString(" }");
1211 if (outer_ != NULL) outer_->PrintTo(f);
1212}
1213
1214const char* Environment::ToCString() const {
1215 char buffer[1024];
1216 BufferFormatter bf(buffer, 1024);
1217 PrintTo(&bf);
1218 return Thread::Current()->zone()->MakeCopyOfString(buffer);
1219}
1220
1221#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
1222
1223const char* Instruction::ToCString() const {
1224 return DebugName();
1225}
1226
1227void FlowGraphPrinter::PrintOneInstruction(Instruction* instr,
1228 bool print_locations) {
1229 UNREACHABLE();
1230}
1231
1232void FlowGraphPrinter::PrintTypeCheck(const ParsedFunction& parsed_function,
1233 TokenPosition token_pos,
1234 Value* value,
1235 const AbstractType& dst_type,
1236 const String& dst_name,
1237 bool eliminated) {
1238 UNREACHABLE();
1239}
1240
1241void FlowGraphPrinter::PrintBlock(BlockEntryInstr* block,
1242 bool print_locations) {
1243 UNREACHABLE();
1244}
1245
1246void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
1247 UNREACHABLE();
1248}
1249
1250void FlowGraphPrinter::PrintICData(const ICData& ic_data,
1251 intptr_t num_checks_to_print) {
1252 UNREACHABLE();
1253}
1254
1255bool FlowGraphPrinter::ShouldPrint(const Function& function) {
1256 return false;
1257}
1258
1259#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
1260
1261} // namespace dart
1262