| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "vm/compiler/backend/il_printer.h" | |
| #include <tuple> | |
| #include "vm/class_id.h" | |
| #include "vm/compiler/api/print_filter.h" | |
| #include "vm/compiler/backend/il.h" | |
| #include "vm/compiler/backend/linearscan.h" | |
| #include "vm/compiler/backend/loops.h" | |
| #include "vm/compiler/backend/range_analysis.h" | |
| #include "vm/compiler/ffi/native_calling_convention.h" | |
| #include "vm/globals.h" | |
| #include "vm/os.h" | |
| #include "vm/parser.h" | |
| namespace dart { | |
| #if defined(INCLUDE_IL_PRINTER) | |
| DEFINE_FLAG(bool, | |
| display_sorted_ic_data, | |
| false, | |
| "Calls display a unary, sorted-by count form of ICData"); | |
| DEFINE_FLAG(bool, print_environments, false, "Print SSA environments."); | |
| DEFINE_FLAG(bool, | |
| print_flow_graph_as_json, | |
| false, | |
| "Use machine readable output when printing IL graphs."); | |
| DEFINE_FLAG(bool, print_redundant_il, false, "Print redundant IL instructions"); | |
| DECLARE_FLAG(bool, trace_inlining_intervals); | |
| static bool IsRedundant(Instruction* instr) { | |
| if (auto constant = instr->AsConstant()) { | |
| return constant->HasSSATemp() && !constant->HasUses(); | |
| } else if (auto move = instr->AsParallelMove()) { | |
| return move->IsRedundant(); | |
| } else { | |
| return false; | |
| } | |
| } | |
| static bool ShouldPrintInstruction(Instruction* instr) { | |
| return FLAG_print_redundant_il || !IsRedundant(instr); | |
| } | |
| class IlTestPrinter : public AllStatic { | |
| public: | |
| static void PrintGraph(const char* phase, FlowGraph* flow_graph) { | |
| JSONWriter writer; | |
| writer.OpenObject(); | |
| writer.PrintProperty("p", phase); | |
| writer.PrintProperty("f", flow_graph->function().ToFullyQualifiedCString()); | |
| writer.OpenArray("b"); | |
| for (auto block : flow_graph->reverse_postorder()) { | |
| PrintBlock(&writer, block); | |
| } | |
| writer.CloseArray(); | |
| const auto& codegen_order = *flow_graph->CodegenBlockOrder(); | |
| if (!codegen_order.is_empty() && | |
| (&codegen_order != &flow_graph->reverse_postorder())) { | |
| writer.OpenArray("cbo"); | |
| const auto block_count = flow_graph->reverse_postorder().length(); | |
| for (auto block : codegen_order) { | |
| writer.PrintValue64((block_count - 1) - block->postorder_number()); | |
| } | |
| writer.CloseArray(); | |
| } | |
| writer.OpenObject("desc"); | |
| AttributesSerializer(&writer).WriteDescriptors(); | |
| writer.CloseObject(); | |
| writer.OpenObject("flags"); | |
| writer.PrintPropertyBool("nnbd", true); | |
| writer.CloseObject(); | |
| writer.CloseObject(); | |
| THR_Print("%s\n", writer.ToCString()); | |
| } | |
| static void PrintBlock(JSONWriter* writer, BlockEntryInstr* block) { | |
| writer->OpenObject(); | |
| writer->PrintProperty64("b", block->block_id()); | |
| writer->PrintProperty("o", block->DebugName()); | |
| if (auto block_with_defs = block->AsBlockEntryWithInitialDefs()) { | |
| if (block_with_defs->initial_definitions() != nullptr && | |
| block_with_defs->initial_definitions()->length() > 0) { | |
| writer->OpenArray("d"); | |
| for (auto defn : *block_with_defs->initial_definitions()) { | |
| if (ShouldPrintInstruction(defn)) { | |
| PrintInstruction(writer, defn); | |
| } | |
| } | |
| writer->CloseArray(); | |
| } | |
| } | |
| if (auto try_entry = block->AsTryEntry()) { | |
| writer->PrintProperty("tryBody", try_entry->try_body()->block_id()); | |
| writer->PrintProperty("catches", try_entry->catch_target()->block_id()); | |
| } | |
| if (auto loop = block->loop_info()) { | |
| writer->OpenArray("ls"); | |
| for (LoopInfo* l = loop; l != nullptr; l = l->outer()) { | |
| writer->PrintValue(l->id()); | |
| } | |
| writer->CloseArray(); | |
| if (loop->header() == block) { | |
| writer->PrintPropertyBool("lh", true); | |
| } | |
| } | |
| writer->OpenArray("is"); | |
| if (auto join = block->AsJoinEntry()) { | |
| for (PhiIterator it(join); !it.Done(); it.Advance()) { | |
| if (ShouldPrintInstruction(it.Current())) { | |
| PrintInstruction(writer, it.Current()); | |
| } | |
| } | |
| } | |
| for (auto instr : block->instructions()) { | |
| if (instr->ArgumentCount() != 0 && instr->GetMoveArguments() != nullptr) { | |
| for (auto move_arg : *(instr->GetMoveArguments())) { | |
| PrintInstruction(writer, move_arg); | |
| } | |
| } | |
| if (ShouldPrintInstruction(instr) && !instr->IsMoveArgument()) { | |
| PrintInstruction(writer, instr); | |
| } | |
| } | |
| writer->CloseArray(); | |
| writer->CloseObject(); | |
| } | |
| static void PrintInstruction(JSONWriter* writer, | |
| Instruction* instr, | |
| const char* name = nullptr) { | |
| writer->OpenObject(name); | |
| if (auto defn = instr->AsDefinition()) { | |
| if (defn->ssa_temp_index() != -1) { | |
| writer->PrintProperty("v", defn->ssa_temp_index()); | |
| } | |
| if (defn->HasType()) { | |
| writer->OpenObject("T"); | |
| defn->Type()->PrintTo(writer); | |
| writer->CloseObject(); | |
| } | |
| } | |
| writer->PrintProperty("o", instr->DebugName()); | |
| if (auto branch = instr->AsBranch()) { | |
| PrintInstruction(writer, branch->condition(), "cc"); | |
| } else { | |
| if (instr->InputCount() != 0) { | |
| writer->OpenArray("i"); | |
| for (intptr_t i = 0; i < instr->InputCount(); i++) { | |
| writer->PrintValue(instr->InputAt(i)->definition()->ssa_temp_index()); | |
| } | |
| writer->CloseArray(); | |
| } else if (instr->ArgumentCount() != 0 && | |
| instr->GetMoveArguments() != nullptr) { | |
| writer->OpenArray("i"); | |
| for (intptr_t i = 0; i < instr->ArgumentCount(); i++) { | |
| writer->PrintValue( | |
| instr->ArgumentValueAt(i)->definition()->ssa_temp_index()); | |
| } | |
| writer->CloseArray(); | |
| } | |
| AttributesSerializer serializer(writer); | |
| instr->Accept(&serializer); | |
| } | |
| if (instr->SuccessorCount() > 0) { | |
| writer->OpenArray("s"); | |
| for (auto succ : instr->successors()) { | |
| writer->PrintValue(succ->block_id()); | |
| } | |
| writer->CloseArray(); | |
| } | |
| writer->CloseObject(); | |
| } | |
| template <typename T> | |
| class HasGetAttributes { | |
| template <typename U> | |
| static std::true_type test(decltype(&U::GetAttributes)); | |
| template <typename U> | |
| static std::false_type test(...); | |
| public: | |
| static constexpr bool value = decltype(test<T>(nullptr))::value; | |
| }; | |
| class AttributesSerializer : public InstructionVisitor { | |
| public: | |
| explicit AttributesSerializer(JSONWriter* writer) : writer_(writer) {} | |
| void WriteDescriptors() { | |
| #define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \ | |
| WriteDescriptor<ShortName##Instr>(#ShortName); | |
| FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION); | |
| #undef DECLARE_VISIT_INSTRUCTION | |
| } | |
| #define DECLARE_VISIT_INSTRUCTION(ShortName, Attrs) \ | |
| virtual void Visit##ShortName(ShortName##Instr* instr) { \ | |
| Write(instr); \ | |
| } | |
| FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION) | |
| #undef DECLARE_VISIT_INSTRUCTION | |
| private: | |
| void WriteAttribute(const char* value) { writer_->PrintValue(value); } | |
| void WriteAttribute(intptr_t value) { writer_->PrintValue(value); } | |
| void WriteAttribute(bool value) { writer_->PrintValueBool(value); } | |
| void WriteAttribute(Token::Kind kind) { | |
| writer_->PrintValue(Token::Str(kind)); | |
| } | |
| void WriteAttribute(Representation rep) { | |
| writer_->PrintValue(RepresentationUtils::ToCString(rep)); | |
| } | |
| static const char* LocationKindAsString(const Location& loc) { | |
| if (loc.IsConstant()) { | |
| return "C"; | |
| } else if (loc.IsPairLocation()) { | |
| auto pair = loc.AsPairLocation(); | |
| return Thread::Current()->zone()->PrintToString( | |
| "(%s, %s)", LocationKindAsString(pair->At(0)), | |
| LocationKindAsString(pair->At(0))); | |
| } else { | |
| switch (loc.kind()) { | |
| case Location::kUnallocated: | |
| return "."; | |
| case Location::kStackSlot: | |
| return "stack(word)"; | |
| case Location::kDoubleStackSlot: | |
| return "stack(f64)"; | |
| case Location::kQuadStackSlot: | |
| return "stack(f128)"; | |
| case Location::kRegister: | |
| return "reg(cpu)"; | |
| case Location::kFpuRegister: | |
| return "reg(fpu)"; | |
| default: | |
| return "?"; | |
| } | |
| } | |
| } | |
| void WriteAttribute(const Location& loc) { | |
| writer_->PrintValue(LocationKindAsString(loc)); | |
| } | |
| void WriteAttribute(const Slot* slot) { writer_->PrintValue(slot->Name()); } | |
| void WriteAttribute(const Function* function) { | |
| writer_->PrintValue(function->QualifiedUserVisibleNameCString()); | |
| } | |
| void WriteAttribute(const Object* obj) { | |
| if (obj->IsNull()) { | |
| writer_->PrintValueNull(); | |
| } else if (obj->IsBool()) { | |
| writer_->PrintValueBool(Bool::Cast(*obj).value()); | |
| } else if (obj->IsInteger()) { | |
| auto value = Integer::Cast(*obj).Value(); | |
| // PrintValue64 and PrintValue will check if integer is representable | |
| // as a JS number, which is too strict. We don't actually need | |
| // such checks because we only load resulting JSON in Dart. | |
| writer_->buffer()->Printf("%" Pd64 "", value); | |
| } else if (obj->IsString()) { | |
| const auto& str = String::Cast(*obj); | |
| writer_->PrintValueStr(str, 0, str.Length()); | |
| } else { | |
| const auto& cls = Class::Handle(obj->clazz()); | |
| writer_->PrintfValue("Instance of %s", cls.UserVisibleNameCString()); | |
| } | |
| } | |
| template <typename... Ts> | |
| void WriteTuple(const std::tuple<Ts...>& tuple) { | |
| std::apply( | |
| [&](Ts const&... elements) { (WriteAttribute(elements), ...); }, | |
| tuple); | |
| } | |
| template <typename T, | |
| typename = typename std::enable_if_t<HasGetAttributes<T>::value>> | |
| void Write(T* instr) { | |
| writer_->OpenArray("d"); | |
| WriteTuple(instr->GetAttributes()); | |
| writer_->CloseArray(); | |
| } | |
| void Write(Instruction* instr) { | |
| // Default, do nothing. | |
| } | |
| void WriteAttributeName(const char* str) { | |
| ASSERT(str != nullptr); | |
| // To simplify the declaring side, we assume the string might be directly | |
| // stringized from one of the following expression forms: | |
| // | |
| // * &name() | |
| // * name() | |
| // * name_ | |
| // | |
| // Remove the non-name parts of the above before printing. | |
| const intptr_t start = str[0] == '&' ? 1 : 0; | |
| intptr_t end = strlen(str); | |
| switch (str[end - 1]) { | |
| case ')': | |
| ASSERT(end >= 2); | |
| ASSERT_EQUAL(str[end - 2], '('); | |
| end -= 2; | |
| break; | |
| case '_': | |
| // Strip off the final _ from a direct private field access. | |
| end -= 1; | |
| break; | |
| default: | |
| break; | |
| } | |
| writer_->PrintValue(str + start, end - start); | |
| } | |
| template <typename... Ts> | |
| void WriteAttributeNames(const std::tuple<Ts...>& tuple) { | |
| std::apply( | |
| [&](Ts const&... elements) { (WriteAttributeName(elements), ...); }, | |
| tuple); | |
| } | |
| template <typename T> | |
| void WriteDescriptor( | |
| const char* name, | |
| typename std::enable_if_t<HasGetAttributes<T>::value>* = nullptr) { | |
| writer_->OpenArray(name); | |
| WriteAttributeNames(T::GetAttributeNames()); | |
| writer_->CloseArray(); | |
| } | |
| template <typename T> | |
| void WriteDescriptor( | |
| const char* name, | |
| typename std::enable_if_t<!HasGetAttributes<T>::value>* = nullptr) {} | |
| JSONWriter* writer_; | |
| }; | |
| }; | |
| bool FlowGraphPrinter::ShouldPrint( | |
| const Function& function, | |
| uint8_t** compiler_pass_filter /* = nullptr */) { | |
| return compiler::PrintFilter::ShouldPrint(function, compiler_pass_filter); | |
| } | |
| void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) { | |
| LogBlock lb; | |
| if (FLAG_print_flow_graph_as_json) { | |
| IlTestPrinter::PrintGraph(phase, flow_graph); | |
| } else { | |
| THR_Print("*** BEGIN CFG\n%s\n", phase); | |
| FlowGraphPrinter printer(*flow_graph); | |
| printer.PrintBlocks(); | |
| THR_Print("*** END CFG\n"); | |
| } | |
| fflush(stdout); | |
| } | |
| void FlowGraphPrinter::PrintBlock(BlockEntryInstr* block, | |
| bool print_locations) { | |
| // Print the block entry. | |
| PrintOneInstruction(block, print_locations); | |
| THR_Print("\n"); | |
| // And all the successors in the block. | |
| for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | |
| Instruction* current = it.Current(); | |
| // Skip redundant parallel moves. | |
| if (!ShouldPrintInstruction(current)) { | |
| continue; | |
| } | |
| PrintOneInstruction(current, print_locations); | |
| THR_Print("\n"); | |
| } | |
| } | |
| void FlowGraphPrinter::PrintBlocks() { | |
| if (!function_.IsNull()) { | |
| THR_Print("==== %s (%s", function_.ToFullyQualifiedCString(), | |
| Function::KindToCString(function_.kind())); | |
| // Output saved arguments descriptor information for dispatchers that | |
| // have it, so it's easy to see which dispatcher this graph represents. | |
| if (function_.HasSavedArgumentsDescriptor()) { | |
| const auto& args_desc_array = Array::Handle(function_.saved_args_desc()); | |
| const ArgumentsDescriptor args_desc(args_desc_array); | |
| THR_Print(", %s", args_desc.ToCString()); | |
| } | |
| THR_Print(")\n"); | |
| } | |
| for (intptr_t i = 0; i < block_order_.length(); ++i) { | |
| PrintBlock(block_order_[i], print_locations_); | |
| } | |
| } | |
| void FlowGraphPrinter::PrintInstruction(Instruction* instr) { | |
| PrintOneInstruction(instr, print_locations_); | |
| } | |
| void FlowGraphPrinter::PrintOneInstruction(Instruction* instr, | |
| bool print_locations) { | |
| char str[4000]; | |
| BufferFormatter f(str, sizeof(str)); | |
| if (FlowGraphAllocator::HasLifetimePosition(instr)) { | |
| f.Printf("%3" Pd ": ", FlowGraphAllocator::GetLifetimePosition(instr)); | |
| } | |
| if (!instr->IsBlockEntry()) { | |
| f.Printf(" "); | |
| } | |
| if (auto* const block = instr->AsBlockEntryWithInitialDefs()) { | |
| block->PrintBlockHeaderTo(&f); | |
| // Output and clear the buffer after each internal definition, as otherwise | |
| // too many internal definitions could cause truncation in the output. | |
| auto callback = [](BaseTextBuffer* f) { | |
| THR_Print("%s", f->buffer()); | |
| f->Clear(); | |
| }; | |
| block->PrintInitialDefinitionsTo(&f, callback); | |
| } else { | |
| instr->PrintTo(&f); | |
| } | |
| if (FLAG_print_environments && (instr->env() != nullptr)) { | |
| instr->env()->PrintTo(&f); | |
| } | |
| if (print_locations && (instr->HasLocs())) { | |
| instr->locs()->PrintTo(&f); | |
| } | |
| if (FLAG_trace_inlining_intervals) { | |
| f.Printf(" iid: %" Pd "", instr->inlining_id()); | |
| } | |
| THR_Print("%s", f.buffer()); | |
| } | |
| void FlowGraphPrinter::PrintTypeCheck(const ParsedFunction& parsed_function, | |
| TokenPosition token_pos, | |
| Value* value, | |
| const AbstractType& dst_type, | |
| const String& dst_name, | |
| bool eliminated) { | |
| const char* compile_type_name = "unknown"; | |
| if (value != nullptr && value->reaching_type_ != nullptr) { | |
| compile_type_name = value->reaching_type_->ToCString(); | |
| } | |
| THR_Print( | |
| "%s type check: compile type %s is %s specific than " | |
| "type '%s' of '%s'.\n", | |
| eliminated ? "Eliminated" : "Generated", compile_type_name, | |
| eliminated ? "more" : "not more", dst_type.NameCString(), | |
| dst_name.ToCString()); | |
| } | |
| static void PrintTargetsHelper(BaseTextBuffer* f, | |
| const CallTargets& targets, | |
| intptr_t num_checks_to_print) { | |
| f->AddString(" Targets["); | |
| f->Printf("%" Pd ": ", targets.length()); | |
| Function& target = Function::Handle(); | |
| if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) || | |
| (num_checks_to_print > targets.length())) { | |
| num_checks_to_print = targets.length(); | |
| } | |
| for (intptr_t i = 0; i < num_checks_to_print; i++) { | |
| const CidRange& range = targets[i]; | |
| const auto target_info = targets.TargetAt(i); | |
| const intptr_t count = target_info->count; | |
| target = target_info->target->ptr(); | |
| if (i > 0) { | |
| f->AddString(" | "); | |
| } | |
| if (range.IsSingleCid()) { | |
| const Class& cls = Class::Handle( | |
| IsolateGroup::Current()->class_table()->At(range.cid_start)); | |
| f->Printf("%s", String::Handle(cls.Name()).ToCString()); | |
| f->Printf(" cid %" Pd " cnt:%" Pd " trgt:'%s'", range.cid_start, count, | |
| target.ToQualifiedCString()); | |
| } else { | |
| const Class& cls = Class::Handle(target.Owner()); | |
| f->Printf("cid %" Pd "-%" Pd " %s", range.cid_start, range.cid_end, | |
| String::Handle(cls.Name()).ToCString()); | |
| f->Printf(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString()); | |
| } | |
| if (target_info->exactness.IsTracking()) { | |
| f->Printf(" %s", target_info->exactness.ToCString()); | |
| } | |
| } | |
| if (num_checks_to_print < targets.length()) { | |
| f->AddString("..."); | |
| } | |
| f->AddString("]"); | |
| } | |
| static void PrintCidsHelper(BaseTextBuffer* f, | |
| const Cids& targets, | |
| intptr_t num_checks_to_print) { | |
| f->AddString(" Cids["); | |
| f->Printf("%" Pd ": ", targets.length()); | |
| if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) || | |
| (num_checks_to_print > targets.length())) { | |
| num_checks_to_print = targets.length(); | |
| } | |
| for (intptr_t i = 0; i < num_checks_to_print; i++) { | |
| const CidRange& range = targets[i]; | |
| if (i > 0) { | |
| f->AddString(" | "); | |
| } | |
| const Class& cls = Class::Handle( | |
| IsolateGroup::Current()->class_table()->At(range.cid_start)); | |
| f->Printf("%s etc. ", String::Handle(cls.Name()).ToCString()); | |
| if (range.IsSingleCid()) { | |
| f->Printf(" cid %" Pd, range.cid_start); | |
| } else { | |
| f->Printf(" cid %" Pd "-%" Pd, range.cid_start, range.cid_end); | |
| } | |
| } | |
| if (num_checks_to_print < targets.length()) { | |
| f->AddString("..."); | |
| } | |
| f->AddString("]"); | |
| } | |
| static void PrintICDataHelper(BaseTextBuffer* f, | |
| const ICData& ic_data, | |
| intptr_t num_checks_to_print) { | |
| f->AddString(" IC["); | |
| if (ic_data.is_tracking_exactness()) { | |
| f->Printf( | |
| "(%s) ", | |
| AbstractType::Handle(ic_data.receivers_static_type()).ToCString()); | |
| } | |
| f->Printf("%" Pd ": ", ic_data.NumberOfChecks()); | |
| Function& target = Function::Handle(); | |
| if ((num_checks_to_print == FlowGraphPrinter::kPrintAll) || | |
| (num_checks_to_print > ic_data.NumberOfChecks())) { | |
| num_checks_to_print = ic_data.NumberOfChecks(); | |
| } | |
| for (intptr_t i = 0; i < num_checks_to_print; i++) { | |
| GrowableArray<intptr_t> class_ids; | |
| ic_data.GetCheckAt(i, &class_ids, &target); | |
| const intptr_t count = ic_data.GetCountAt(i); | |
| if (i > 0) { | |
| f->AddString(" | "); | |
| } | |
| for (intptr_t k = 0; k < class_ids.length(); k++) { | |
| if (k > 0) { | |
| f->AddString(", "); | |
| } | |
| const Class& cls = Class::Handle( | |
| IsolateGroup::Current()->class_table()->At(class_ids[k])); | |
| f->Printf("%s", String::Handle(cls.Name()).ToCString()); | |
| } | |
| f->Printf(" cnt:%" Pd " trgt:'%s'", count, target.ToQualifiedCString()); | |
| if (ic_data.is_tracking_exactness()) { | |
| f->Printf(" %s", ic_data.GetExactnessAt(i).ToCString()); | |
| } | |
| } | |
| if (num_checks_to_print < ic_data.NumberOfChecks()) { | |
| f->AddString("..."); | |
| } | |
| f->AddString("]"); | |
| } | |
| static void PrintICDataSortedHelper(BaseTextBuffer* f, | |
| const ICData& ic_data_orig) { | |
| const ICData& ic_data = | |
| ICData::Handle(ic_data_orig.AsUnaryClassChecksSortedByCount()); | |
| f->Printf(" IC[n:%" Pd "; ", ic_data.NumberOfChecks()); | |
| for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { | |
| const intptr_t count = ic_data.GetCountAt(i); | |
| const intptr_t cid = ic_data.GetReceiverClassIdAt(i); | |
| const Class& cls = | |
| Class::Handle(IsolateGroup::Current()->class_table()->At(cid)); | |
| f->Printf("%s : %" Pd ", ", String::Handle(cls.Name()).ToCString(), count); | |
| } | |
| f->AddString("]"); | |
| } | |
| void FlowGraphPrinter::PrintICData(const ICData& ic_data, | |
| intptr_t num_checks_to_print) { | |
| char buffer[1024]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| PrintICDataHelper(&f, ic_data, num_checks_to_print); | |
| THR_Print("%s ", buffer); | |
| const Array& a = Array::Handle(ic_data.arguments_descriptor()); | |
| THR_Print(" arg-desc %" Pd "\n", a.Length()); | |
| } | |
| void FlowGraphPrinter::PrintCidRangeData(const CallTargets& targets, | |
| intptr_t num_checks_to_print) { | |
| char buffer[1024]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| PrintTargetsHelper(&f, targets, num_checks_to_print); | |
| THR_Print("%s ", buffer); | |
| // TODO(erikcorry): Print args descriptor. | |
| } | |
| static void PrintUse(BaseTextBuffer* f, const Definition& definition) { | |
| if (definition.HasSSATemp()) { | |
| f->Printf("v%" Pd "", definition.ssa_temp_index()); | |
| } else if (definition.HasTemp()) { | |
| f->Printf("t%" Pd "", definition.temp_index()); | |
| } | |
| } | |
| const char* Instruction::ToCString() const { | |
| char buffer[1024]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| PrintTo(&f); | |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); | |
| } | |
| void Instruction::PrintTo(BaseTextBuffer* f) const { | |
| if (GetDeoptId() != DeoptId::kNone) { | |
| f->Printf("%s:%" Pd "(", DebugName(), GetDeoptId()); | |
| } else { | |
| f->Printf("%s(", DebugName()); | |
| } | |
| PrintOperandsTo(f); | |
| f->AddString(")"); | |
| } | |
| void Instruction::PrintOperandsTo(BaseTextBuffer* f) const { | |
| for (int i = 0; i < InputCount(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| if (InputAt(i) != nullptr) InputAt(i)->PrintTo(f); | |
| } | |
| } | |
| void Definition::PrintTo(BaseTextBuffer* f) const { | |
| PrintUse(f, *this); | |
| if (HasSSATemp() || HasTemp()) f->AddString(" <- "); | |
| if (GetDeoptId() != DeoptId::kNone) { | |
| f->Printf("%s:%" Pd "(", DebugName(), GetDeoptId()); | |
| } else { | |
| f->Printf("%s(", DebugName()); | |
| } | |
| PrintOperandsTo(f); | |
| f->AddString(")"); | |
| if (range_ != nullptr) { | |
| f->AddString(" "); | |
| range_->PrintTo(f); | |
| } | |
| if (representation() != kNoRepresentation && representation() != kTagged) { | |
| f->Printf(" %s", RepresentationUtils::ToCString(representation())); | |
| } else if (type_ != nullptr) { | |
| f->AddString(" "); | |
| type_->PrintTo(f); | |
| } | |
| } | |
| void CheckNullInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| switch (exception_type()) { | |
| case kNoSuchMethod: | |
| f->AddString(", NoSuchMethodError"); | |
| break; | |
| case kArgumentError: | |
| f->AddString(", ArgumentError"); | |
| break; | |
| case kCastError: | |
| f->AddString(", CastError"); | |
| break; | |
| } | |
| } | |
| void Definition::PrintOperandsTo(BaseTextBuffer* f) const { | |
| for (int i = 0; i < InputCount(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| if (InputAt(i) != nullptr) { | |
| InputAt(i)->PrintTo(f); | |
| } | |
| } | |
| } | |
| void RedefinitionInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| if (constrained_type_ != nullptr) { | |
| f->Printf(" ^ %s", constrained_type_->ToCString()); | |
| } | |
| } | |
| void ReachabilityFenceInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| } | |
| const char* Value::ToCString() const { | |
| char buffer[1024]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| PrintTo(&f); | |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); | |
| } | |
| void Value::PrintTo(BaseTextBuffer* f) const { | |
| PrintUse(f, *definition()); | |
| if ((reaching_type_ != nullptr) && (reaching_type_ != definition()->type_)) { | |
| f->AddString(" "); | |
| reaching_type_->PrintTo(f); | |
| } | |
| } | |
| void ConstantInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const char* cstr = value().ToCString(); | |
| const char* first_non_printable = cstr; | |
| while (isprint(static_cast<unsigned char>(*first_non_printable)) != 0) { | |
| first_non_printable++; | |
| } | |
| if (*first_non_printable == 0) { | |
| f->Printf("#%s", cstr); | |
| } else { | |
| const intptr_t pos = first_non_printable - cstr; | |
| char* buffer = Thread::Current()->zone()->Alloc<char>(pos + 1); | |
| strncpy(buffer, cstr, pos); | |
| buffer[pos] = '\0'; | |
| f->Printf("#%s...", buffer); | |
| } | |
| } | |
| void ConstraintInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| f->AddString(" ^ "); | |
| constraint()->PrintTo(f); | |
| } | |
| void Range::PrintTo(BaseTextBuffer* f) const { | |
| f->AddString("["); | |
| min_.PrintTo(f); | |
| f->AddString(", "); | |
| max_.PrintTo(f); | |
| f->AddString("]"); | |
| } | |
| const char* Range::ToCString(const Range* range) { | |
| if (range == nullptr) return "[_|_, _|_]"; | |
| char buffer[256]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| range->PrintTo(&f); | |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); | |
| } | |
| void RangeBoundary::PrintTo(BaseTextBuffer* f) const { | |
| switch (kind_) { | |
| case kSymbol: | |
| f->Printf("v%" Pd "", | |
| reinterpret_cast<Definition*>(value_)->ssa_temp_index()); | |
| if (offset_ != 0) f->Printf("%+" Pd64 "", offset_); | |
| break; | |
| case kConstant: | |
| f->Printf("%" Pd64 "", value_); | |
| break; | |
| case kUnknown: | |
| f->AddString("_|_"); | |
| break; | |
| } | |
| } | |
| const char* RangeBoundary::ToCString() const { | |
| char buffer[256]; | |
| BufferFormatter f(buffer, sizeof(buffer)); | |
| PrintTo(&f); | |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); | |
| } | |
| void MakeTempInstr::PrintOperandsTo(BaseTextBuffer* f) const {} | |
| void DropTempsInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%" Pd "", num_temps()); | |
| if (value() != nullptr) { | |
| f->AddString(", "); | |
| value()->PrintTo(f); | |
| } | |
| } | |
| void AssertAssignableInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| f->AddString(", "); | |
| dst_type()->PrintTo(f); | |
| f->Printf(", '%s',", dst_name().ToCString()); | |
| f->AddString(" instantiator_type_args("); | |
| instantiator_type_arguments()->PrintTo(f); | |
| f->AddString("), function_type_args("); | |
| function_type_arguments()->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void AssertSubtypeInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| sub_type()->PrintTo(f); | |
| f->AddString(", "); | |
| super_type()->PrintTo(f); | |
| f->AddString(", "); | |
| dst_name()->PrintTo(f); | |
| f->AddString(", instantiator_type_args("); | |
| instantiator_type_arguments()->PrintTo(f); | |
| f->AddString("), function_type_args("); | |
| function_type_arguments()->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void ClosureCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (FLAG_precompiled_mode) { | |
| f->AddString(" closure="); | |
| } else { | |
| f->AddString(" function="); | |
| } | |
| InputAt(InputCount() - 1)->PrintTo(f); | |
| f->Printf("<%" Pd ">", type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| } | |
| void InstanceCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf(" %s<%" Pd ">", function_name().ToCString(), type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| if (HasICData()) { | |
| if (FLAG_display_sorted_ic_data) { | |
| PrintICDataSortedHelper(f, *ic_data()); | |
| } else { | |
| PrintICDataHelper(f, *ic_data(), FlowGraphPrinter::kPrintAll); | |
| } | |
| } | |
| if (result_type() != nullptr) { | |
| f->Printf(", result_type = %s", result_type()->ToCString()); | |
| } | |
| if (entry_kind() == Code::EntryKind::kUnchecked) { | |
| f->AddString(" using unchecked entrypoint"); | |
| } | |
| } | |
| void PolymorphicInstanceCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf(" %s<%" Pd ">", function_name().ToCString(), type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| PrintTargetsHelper(f, targets_, FlowGraphPrinter::kPrintAll); | |
| if (complete()) { | |
| f->AddString(" COMPLETE"); | |
| } | |
| if (entry_kind() == Code::EntryKind::kUnchecked) { | |
| f->AddString(" using unchecked entrypoint"); | |
| } | |
| } | |
| void DispatchTableCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const String& name = | |
| String::Handle(interface_target().QualifiedUserVisibleName()); | |
| f->AddString(" cid="); | |
| class_id()->PrintTo(f); | |
| f->Printf(" %s<%" Pd ">", name.ToCString(), type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| } | |
| void StrictCompareInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", Token::Str(kind())); | |
| left()->PrintTo(f); | |
| f->AddString(", "); | |
| right()->PrintTo(f); | |
| if (needs_number_check()) { | |
| f->Printf(", with number check"); | |
| } | |
| } | |
| void TestCidsInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| f->Printf(" %s [", Token::Str(kind())); | |
| intptr_t length = cid_results().length(); | |
| for (intptr_t i = 0; i < length; i += 2) { | |
| f->Printf("0x%" Px ":%s ", cid_results()[i], | |
| cid_results()[i + 1] == 0 ? "false" : "true"); | |
| } | |
| f->AddString("] "); | |
| if (CanDeoptimize()) { | |
| ASSERT(deopt_id() != DeoptId::kNone); | |
| f->AddString("else deoptimize "); | |
| } else { | |
| ASSERT(deopt_id() == DeoptId::kNone); | |
| f->Printf("else %s ", cid_results()[length - 1] != 0 ? "false" : "true"); | |
| } | |
| } | |
| void TestRangeInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| f->Printf(" %s [%" Pd "-%" Pd "]", kind() == Token::kIS ? "in" : "not in", | |
| lower_, upper_); | |
| } | |
| void EqualityCompareInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| left()->PrintTo(f); | |
| f->Printf(" %s ", Token::Str(kind())); | |
| right()->PrintTo(f); | |
| } | |
| void StaticCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf(" %s<%" Pd "> ", String::Handle(function().name()).ToCString(), | |
| type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| if (entry_kind() == Code::EntryKind::kUnchecked) { | |
| f->AddString(", using unchecked entrypoint"); | |
| } | |
| if (function().recognized_kind() != MethodRecognizer::kUnknown) { | |
| f->Printf(", recognized_kind = %s", | |
| MethodRecognizer::KindToCString(function().recognized_kind())); | |
| } | |
| if (result_type() != nullptr) { | |
| f->Printf(", result_type = %s", result_type()->ToCString()); | |
| } | |
| } | |
| void CachableIdempotentCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf(" %s<%" Pd "> ", String::Handle(function().name()).ToCString(), | |
| type_args_len()); | |
| for (intptr_t i = 0; i < ArgumentCount(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| ArgumentValueAt(i)->PrintTo(f); | |
| } | |
| } | |
| void LoadLocalInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s @%d", local().name().ToCString(), local().index().value()); | |
| } | |
| void StoreLocalInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s @%d, ", local().name().ToCString(), local().index().value()); | |
| value()->PrintTo(f); | |
| } | |
| void NativeCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s", native_name().ToCString()); | |
| } | |
| void GuardFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s %s, ", String::Handle(field().name()).ToCString(), | |
| field().GuardedPropertiesAsCString()); | |
| value()->PrintTo(f); | |
| } | |
| void CheckFieldImmutabilityInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s , ", String::Handle(field().name()).ToCString()); | |
| value()->PrintTo(f); | |
| } | |
| void StoreFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| instance()->PrintTo(f); | |
| f->Printf(" . %s = ", slot().Name()); | |
| value()->PrintTo(f); | |
| if (slot().representation() != kTagged) { | |
| f->Printf(" <%s>", RepresentationUtils::ToCString(slot().representation())); | |
| } | |
| // Here, we just print the value of the enum field. We would prefer to get | |
| // the final decision on whether a store barrier will be emitted by calling | |
| // ShouldEmitStoreBarrier(), but that can change parts of the flow graph. | |
| if (emit_store_barrier_ == kNoStoreBarrier) { | |
| f->AddString(", NoStoreBarrier"); | |
| } | |
| if (stores_inner_pointer() == InnerPointerAccess::kMayBeInnerPointer) { | |
| f->AddString(", MayStoreInnerPointer"); | |
| } | |
| if (memory_order_ == compiler::Assembler::kRelease) { | |
| f->AddString(", Release"); | |
| } | |
| } | |
| void IfThenElseInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| condition()->PrintOperandsTo(f); | |
| f->Printf(" ? %" Pd " : %" Pd, if_true_, if_false_); | |
| } | |
| void LoadStaticFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s", String::Handle(field().name()).ToCString()); | |
| if (throws_access_error()) { | |
| f->AddString(", ThrowsAccessError"); | |
| } | |
| if (calls_initializer()) { | |
| f->AddString(", CallsInitializer"); | |
| } | |
| if (throw_exception_on_initialization()) { | |
| f->AddString(", ThrowExceptionOnInitialization"); | |
| } | |
| } | |
| void StoreStaticFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", String::Handle(field().name()).ToCString()); | |
| value()->PrintTo(f); | |
| } | |
| void InstanceOfInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| f->Printf(" IS %s,", type().NameCString()); | |
| f->AddString(" instantiator_type_args("); | |
| instantiator_type_arguments()->PrintTo(f); | |
| f->AddString("), function_type_args("); | |
| function_type_arguments()->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void RelationalOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", Token::Str(kind())); | |
| left()->PrintTo(f); | |
| f->AddString(", "); | |
| right()->PrintTo(f); | |
| } | |
| void AllocationInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| if (Identity().IsNotAliased()) { | |
| if (InputCount() > 0) { | |
| f->AddString(", "); | |
| } | |
| f->AddString("<not-aliased>"); | |
| } | |
| } | |
| void AllocateObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("cls=%s", cls().ScrubbedNameCString()); | |
| if (InputCount() > 0 || Identity().IsNotAliased()) { | |
| f->AddString(", "); | |
| } | |
| AllocationInstr::PrintOperandsTo(f); | |
| } | |
| void MaterializeObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s", cls_.ScrubbedNameCString()); | |
| for (intptr_t i = 0; i < InputCount(); i++) { | |
| f->AddString(", "); | |
| f->Printf("%s: ", slots_[i]->Name()); | |
| InputAt(i)->PrintTo(f); | |
| } | |
| } | |
| void LoadFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| instance()->PrintTo(f); | |
| f->Printf(" . %s%s", slot().Name(), IsImmutableLoad() ? " {final}" : ""); | |
| if (throws_access_error()) { | |
| f->AddString(", ThrowsAccessError"); | |
| } | |
| if (calls_initializer()) { | |
| f->AddString(", CallsInitializer"); | |
| } | |
| if (loads_inner_pointer() == InnerPointerAccess::kMayBeInnerPointer) { | |
| f->AddString(", MayLoadInnerPointer"); | |
| } | |
| if (memory_order_ == compiler::Assembler::kAcquire) { | |
| f->AddString(", Acquire"); | |
| } | |
| } | |
| void LoadUntaggedInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| object()->PrintTo(f); | |
| f->Printf(", %" Pd, offset()); | |
| } | |
| void CalculateElementAddressInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| f->Printf(", index_scale=%" Pd "", index_scale()); | |
| } | |
| void InstantiateTypeInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const String& type_name = String::Handle(type().Name()); | |
| f->Printf("%s,", type_name.ToCString()); | |
| f->AddString(" instantiator_type_args("); | |
| instantiator_type_arguments()->PrintTo(f); | |
| f->AddString("), function_type_args("); | |
| function_type_arguments()->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void InstantiateTypeArgumentsInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| type_arguments()->PrintTo(f); | |
| f->AddString(", instantiator_type_args("); | |
| instantiator_type_arguments()->PrintTo(f); | |
| f->AddString("), function_type_args("); | |
| function_type_arguments()->PrintTo(f); | |
| f->Printf(")"); | |
| if (!instantiator_class().IsNull()) { | |
| f->Printf(", instantiator_class(%s)", instantiator_class().ToCString()); | |
| } | |
| } | |
| void AllocateContextInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("num_variables=%" Pd "", num_context_variables()); | |
| if (InputCount() > 0 || Identity().IsNotAliased()) { | |
| f->AddString(", "); | |
| } | |
| TemplateAllocation::PrintOperandsTo(f); | |
| } | |
| void AllocateUninitializedContextInstr::PrintOperandsTo( | |
| BaseTextBuffer* f) const { | |
| f->Printf("num_variables=%" Pd "", num_context_variables()); | |
| if (InputCount() > 0 || Identity().IsNotAliased()) { | |
| f->AddString(", "); | |
| } | |
| TemplateAllocation::PrintOperandsTo(f); | |
| } | |
| void TruncDivModInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void ExtractNthOutputInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("Extract %" Pd " from ", index()); | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void UnboxLaneInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| f->Printf(", lane %" Pd, lane()); | |
| } | |
| void BoxLanesInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void UnaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", Token::Str(op_kind())); | |
| value()->PrintTo(f); | |
| } | |
| void BinaryIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s", Token::Str(op_kind())); | |
| if (is_truncating()) { | |
| f->AddString(" [tr]"); | |
| } else if (!can_overflow()) { | |
| f->AddString(" [-o]"); | |
| } | |
| f->AddString(", "); | |
| left()->PrintTo(f); | |
| f->AddString(", "); | |
| right()->PrintTo(f); | |
| } | |
| void BinaryDoubleOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", Token::Str(op_kind())); | |
| left()->PrintTo(f); | |
| f->AddString(", "); | |
| right()->PrintTo(f); | |
| } | |
| void DoubleTestOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| switch (op_kind()) { | |
| case MethodRecognizer::kDouble_getIsNaN: | |
| f->AddString("IsNaN "); | |
| break; | |
| case MethodRecognizer::kDouble_getIsInfinite: | |
| f->AddString("IsInfinite "); | |
| break; | |
| case MethodRecognizer::kDouble_getIsNegative: | |
| f->AddString("IsNegative "); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| value()->PrintTo(f); | |
| } | |
| static const char* const simd_op_kind_string[] = { | |
| #define CASE(Arity, Mask, Name, ...) #Name, | |
| SIMD_OP_LIST(CASE, CASE) | |
| #undef CASE | |
| }; | |
| void SimdOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s", simd_op_kind_string[kind()]); | |
| if (HasMask()) { | |
| f->Printf(", mask = %" Pd "", mask()); | |
| } | |
| for (intptr_t i = 0; i < InputCount(); i++) { | |
| f->AddString(", "); | |
| InputAt(i)->PrintTo(f); | |
| } | |
| } | |
| void UnaryDoubleOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", Token::Str(op_kind())); | |
| value()->PrintTo(f); | |
| } | |
| void LoadClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (!input_can_be_smi_) { | |
| f->AddString("<non-smi> "); | |
| } | |
| object()->PrintTo(f); | |
| } | |
| void CheckClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| const Class& cls = Class::Handle( | |
| IsolateGroup::Current()->class_table()->At(cids().cid_start)); | |
| const String& name = String::Handle(cls.ScrubbedName()); | |
| if (cids().IsSingleCid()) { | |
| f->Printf(", %s", name.ToCString()); | |
| } else { | |
| const Class& cls2 = Class::Handle( | |
| IsolateGroup::Current()->class_table()->At(cids().cid_end)); | |
| const String& name2 = String::Handle(cls2.ScrubbedName()); | |
| f->Printf(", cid %" Pd "-%" Pd " %s-%s", cids().cid_start, cids().cid_end, | |
| name.ToCString(), name2.ToCString()); | |
| } | |
| } | |
| void HashIntegerOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (smi_) { | |
| f->AddString("smi "); | |
| } | |
| value()->PrintTo(f); | |
| } | |
| void CheckClassInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| value()->PrintTo(f); | |
| PrintCidsHelper(f, cids_, FlowGraphPrinter::kPrintAll); | |
| if (IsNullCheck()) { | |
| f->AddString(" nullcheck"); | |
| } | |
| } | |
| void CheckConditionInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| condition()->PrintOperandsTo(f); | |
| } | |
| void InvokeMathCFunctionInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s, ", MethodRecognizer::KindToCString(recognized_kind_)); | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void TsanFuncEntryExitInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (kind_ == kEntry) { | |
| f->AddString("entry"); | |
| } else { | |
| f->AddString("exit"); | |
| } | |
| } | |
| void SanReadWriteInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| instance()->PrintTo(f); | |
| f->Printf(" . %s", slot().Name()); | |
| if (kind_ == kRead) { | |
| f->AddString(", read"); | |
| } else { | |
| f->AddString(", write"); | |
| } | |
| } | |
| void SanReadWriteIndexedInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| array()->PrintTo(f); | |
| f->AddString(", "); | |
| index()->PrintTo(f); | |
| if (kind_ == kRead) { | |
| f->AddString(", read"); | |
| } else { | |
| f->AddString(", write"); | |
| } | |
| } | |
| void GraphEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[graph]:%" Pd, block_id(), GetDeoptId()); | |
| } | |
| static void WriteLoopInfo(const BlockEntryInstr* entry, BaseTextBuffer* f) { | |
| for (LoopInfo* l = entry->loop_info(); l != nullptr; l = l->outer()) { | |
| f->Printf(" loop(%" Pd "%s)", l->id(), l->header() == entry ? ", h" : ""); | |
| } | |
| } | |
| void JoinEntryInstr::PrintTo(BaseTextBuffer* f) const { | |
| if (try_index() != kInvalidTryIndex) { | |
| f->Printf("B%" Pd "[join try_idx %" Pd "]:%" Pd " pred(", block_id(), | |
| try_index(), GetDeoptId()); | |
| } else { | |
| f->Printf("B%" Pd "[join]:%" Pd " pred(", block_id(), GetDeoptId()); | |
| } | |
| for (intptr_t i = 0; i < predecessors_.length(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| f->Printf("B%" Pd, predecessors_[i]->block_id()); | |
| } | |
| f->AddString(")"); | |
| WriteLoopInfo(this, f); | |
| if (phis_ != nullptr) { | |
| f->AddString(" {"); | |
| for (intptr_t i = 0; i < phis_->length(); ++i) { | |
| if ((*phis_)[i] == nullptr) continue; | |
| f->AddString("\n "); | |
| (*phis_)[i]->PrintTo(f); | |
| } | |
| f->AddString("\n}"); | |
| } | |
| if (HasParallelMove()) { | |
| f->AddString(" "); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void IndirectEntryInstr::PrintTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[join indirect", block_id()); | |
| if (try_index() != kInvalidTryIndex) { | |
| f->Printf(" try_idx %" Pd, try_index()); | |
| } | |
| f->Printf("]:%" Pd " pred(", GetDeoptId()); | |
| for (intptr_t i = 0; i < predecessors_.length(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| f->Printf("B%" Pd, predecessors_[i]->block_id()); | |
| } | |
| f->AddString(")"); | |
| if (phis_ != nullptr) { | |
| f->AddString(" {"); | |
| for (intptr_t i = 0; i < phis_->length(); ++i) { | |
| if ((*phis_)[i] == nullptr) continue; | |
| f->AddString("\n "); | |
| (*phis_)[i]->PrintTo(f); | |
| } | |
| f->AddString("\n}"); | |
| } | |
| if (HasParallelMove()) { | |
| f->AddString(" "); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void PhiInstr::PrintTo(BaseTextBuffer* f) const { | |
| f->Printf("v%" Pd " <- phi(", ssa_temp_index()); | |
| for (intptr_t i = 0; i < inputs_.length(); ++i) { | |
| if (inputs_[i] != nullptr) inputs_[i]->PrintTo(f); | |
| if (i < inputs_.length() - 1) f->AddString(", "); | |
| } | |
| f->AddString(")"); | |
| f->AddString(is_alive() ? " alive" : " dead"); | |
| if (range_ != nullptr) { | |
| f->AddString(" "); | |
| range_->PrintTo(f); | |
| } | |
| if (representation() != kNoRepresentation && representation() != kTagged) { | |
| f->Printf(" %s", RepresentationUtils::ToCString(representation())); | |
| } | |
| if (HasType()) { | |
| f->Printf(" %s", TypeAsCString()); | |
| } | |
| } | |
| void UnboxIntegerInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (value_mode() == ValueMode::kCheckType) { | |
| f->AddString("[check-type], "); | |
| } | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void IntConverterInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s->%s, ", RepresentationUtils::ToCString(from()), | |
| RepresentationUtils::ToCString(to())); | |
| Definition::PrintOperandsTo(f); | |
| } | |
| void BitCastInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| f->Printf(" (%s -> %s)", RepresentationUtils::ToCString(from()), | |
| RepresentationUtils::ToCString(to())); | |
| } | |
| void ParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%" Pd " @%s", env_index(), location().ToCString()); | |
| } | |
| void CheckStackOverflowInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("stack=%" Pd ", loop=%" Pd, stack_depth(), loop_depth()); | |
| } | |
| void TargetEntryInstr::PrintTo(BaseTextBuffer* f) const { | |
| if (try_index() != kInvalidTryIndex) { | |
| f->Printf("B%" Pd "[target try_idx %" Pd "]:%" Pd, block_id(), try_index(), | |
| GetDeoptId()); | |
| } else { | |
| f->Printf("B%" Pd "[target]:%" Pd, block_id(), GetDeoptId()); | |
| } | |
| WriteLoopInfo(this, f); | |
| if (HasParallelMove()) { | |
| f->AddString(" "); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void BlockEntryWithInitialDefs::PrintTo(BaseTextBuffer* f) const { | |
| PrintBlockHeaderTo(f); | |
| PrintInitialDefinitionsTo(f, [](BaseTextBuffer* f) {}); | |
| } | |
| void BlockEntryWithInitialDefs::PrintInitialDefinitionsTo( | |
| BaseTextBuffer* f, | |
| std::function<void(BaseTextBuffer* f)> callback) const { | |
| const GrowableArray<Definition*>& defns = initial_definitions_; | |
| if (defns.length() > 0) { | |
| f->AddString(" {"); | |
| for (intptr_t i = 0; i < defns.length(); ++i) { | |
| Definition* const def = defns[i]; | |
| // Skip constants which are not used in the graph. | |
| if (!ShouldPrintInstruction(def)) { | |
| continue; | |
| } | |
| f->AddString("\n "); | |
| def->PrintTo(f); | |
| callback(f); | |
| } | |
| f->AddString("\n}"); | |
| } | |
| } | |
| void OsrEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[osr entry]:%" Pd " stack_depth=%" Pd, block_id(), | |
| GetDeoptId(), stack_depth()); | |
| if (HasParallelMove()) { | |
| f->AddString("\n"); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void FunctionEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[function entry]:%" Pd, block_id(), GetDeoptId()); | |
| if (HasParallelMove()) { | |
| f->AddString("\n"); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void NativeEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[native function entry]:%" Pd, block_id(), GetDeoptId()); | |
| if (HasParallelMove()) { | |
| f->AddString("\n"); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void FfiCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->AddString(" pointer="); | |
| InputAt(TargetAddressIndex())->PrintTo(f); | |
| if (marshaller_.ReturnsCompound()) { | |
| f->AddString(", compound_return_typed_data="); | |
| InputAt(CompoundReturnTypedDataIndex())->PrintTo(f); | |
| } | |
| intptr_t def_index = 0; | |
| for (intptr_t arg_index = 0; arg_index < marshaller_.num_args(); | |
| arg_index++) { | |
| const auto& arg_location = marshaller_.Location(arg_index); | |
| const bool is_compound = arg_location.container_type().IsCompound(); | |
| const intptr_t num_defs = marshaller_.NumDefinitions(arg_index); | |
| f->AddString(", "); | |
| if (is_compound) f->AddString("("); | |
| for (intptr_t i = 0; i < num_defs; i++) { | |
| InputAt(def_index)->PrintTo(f); | |
| if ((i + 1) < num_defs) f->AddString(", "); | |
| def_index++; | |
| } | |
| if (is_compound) f->AddString(")"); | |
| f->AddString(" (@"); | |
| arg_location.PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| } | |
| void LeafRuntimeCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->AddString("target_address="); | |
| InputAt(TargetAddressIndex())->PrintTo(f); | |
| for (intptr_t i = 0, n = argument_representations_.length(); i < n; ++i) { | |
| f->AddString(", "); | |
| InputAt(i)->PrintTo(f); | |
| } | |
| } | |
| void NativeReturnInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| if (marshaller_.NumReturnDefinitions() == 1) { | |
| InputAt(0)->PrintTo(f); | |
| } else { | |
| ASSERT_EQUAL(marshaller_.NumReturnDefinitions(), 2); | |
| f->AddString("("); | |
| InputAt(0)->PrintTo(f); | |
| f->AddString(", "); | |
| InputAt(1)->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| f->AddString(" (@"); | |
| marshaller_.Location(compiler::ffi::kResultIndex).PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void NativeParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| // Where the calling convention puts it. | |
| marshaller_.Location(marshaller_.ArgumentIndex(def_index_)).PrintTo(f); | |
| f->AddString(" at "); | |
| // Where the arguments are when pushed on the stack. | |
| marshaller_.NativeLocationOfNativeParameter(def_index_).PrintTo(f); | |
| } | |
| void TryEntryInstr::PrintTo(BaseTextBuffer* f) const { | |
| f->Printf("B%" Pd "[try_entry try_idx %" Pd "]:%" Pd " try_body:[B%" Pd | |
| "] catches:[B%" Pd "] pred(", | |
| block_id(), try_index(), GetDeoptId(), try_body_->block_id(), | |
| catch_target_->block_id()); | |
| for (intptr_t i = 0; i < PredecessorCount(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| f->Printf("B%" Pd, PredecessorAt(i)->block_id()); | |
| } | |
| f->AddString(")"); | |
| if (phis() != nullptr) { | |
| f->AddString(" {"); | |
| for (intptr_t i = 0; i < phis()->length(); ++i) { | |
| if ((*phis())[i] == nullptr) continue; | |
| f->AddString("\n "); | |
| (*phis())[i]->PrintTo(f); | |
| } | |
| f->AddString("\n}"); | |
| } | |
| if (HasParallelMove()) { | |
| f->AddString(" "); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void CatchBlockEntryInstr::PrintBlockHeaderTo(BaseTextBuffer* f) const { | |
| if (try_index() != kInvalidTryIndex) { | |
| f->Printf("B%" Pd "[catch catch_try_index %" Pd " try_idx %" Pd "]", | |
| block_id(), catch_try_index(), try_index()); | |
| } else { | |
| f->Printf("B%" Pd "[catch catch_try_index %" Pd "]", block_id(), | |
| catch_try_index()); | |
| } | |
| if (HasParallelMove()) { | |
| f->AddString("\n"); | |
| parallel_move()->PrintTo(f); | |
| } | |
| } | |
| void LoadIndexedUnsafeInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s[", RegisterNames::RegisterName(base_reg())); | |
| index()->PrintTo(f); | |
| f->Printf(" + %" Pd "]", offset()); | |
| } | |
| void StoreIndexedUnsafeInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s[", RegisterNames::RegisterName(base_reg())); | |
| index()->PrintTo(f); | |
| f->Printf(" + %" Pd "], ", offset()); | |
| value()->PrintTo(f); | |
| } | |
| void LoadIndexedInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| auto& cls = | |
| Class::Handle(IsolateGroup::Current()->class_table()->At(class_id())); | |
| if (!cls.IsNull()) { | |
| f->Printf("[%s] ", cls.ScrubbedNameCString()); | |
| } else { | |
| f->Printf("[cid %" Pd "] ", class_id()); | |
| } | |
| Instruction::PrintOperandsTo(f); | |
| } | |
| void StoreIndexedInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| auto& cls = | |
| Class::Handle(IsolateGroup::Current()->class_table()->At(class_id())); | |
| if (!cls.IsNull()) { | |
| f->Printf("[%s] ", cls.ScrubbedNameCString()); | |
| } else { | |
| f->Printf("[cid %" Pd "] ", class_id()); | |
| } | |
| Instruction::PrintOperandsTo(f); | |
| if (!ShouldEmitStoreBarrier()) { | |
| f->AddString(", NoStoreBarrier"); | |
| } | |
| } | |
| void MemoryCopyInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Instruction::PrintOperandsTo(f); | |
| auto& cls = | |
| Class::Handle(IsolateGroup::Current()->class_table()->At(dest_cid_)); | |
| if (!cls.IsNull()) { | |
| f->Printf(", dest_cid=%s (%d)", cls.ScrubbedNameCString(), dest_cid_); | |
| } else { | |
| f->Printf(", dest_cid=%d", dest_cid_); | |
| } | |
| if (dest()->definition()->representation() == kUntagged) { | |
| f->Printf(" [untagged]"); | |
| } | |
| cls = IsolateGroup::Current()->class_table()->At(src_cid_); | |
| if (!cls.IsNull()) { | |
| f->Printf(", src_cid=%s (%d)", cls.ScrubbedNameCString(), src_cid_); | |
| } else { | |
| f->Printf(", src_cid=%d", src_cid_); | |
| } | |
| if (src()->definition()->representation() == kUntagged) { | |
| f->Printf(" [untagged]"); | |
| } | |
| if (element_size() != 1) { | |
| f->Printf(", element_size=%" Pd "", element_size()); | |
| } | |
| if (unboxed_inputs()) { | |
| f->AddString(", unboxed_inputs"); | |
| } | |
| if (can_overlap()) { | |
| f->AddString(", can_overlap"); | |
| } | |
| } | |
| void TailCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const char* name = "<unknown code>"; | |
| if (code_.IsStubCode()) { | |
| name = StubCode::NameOfStub(code_.EntryPoint()); | |
| } else { | |
| const Object& owner = Object::Handle(code_.owner()); | |
| if (owner.IsFunction()) { | |
| name = Function::Handle(Function::RawCast(owner.ptr())) | |
| .ToFullyQualifiedCString(); | |
| } | |
| } | |
| f->Printf("%s(", name); | |
| InputAt(0)->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void Call1ArgStubInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const char* name = ""; | |
| switch (stub_id_) { | |
| case StubId::kCloneSuspendState: | |
| name = "CloneSuspendState"; | |
| break; | |
| case StubId::kInitAsync: | |
| name = "InitAsync"; | |
| break; | |
| case StubId::kInitAsyncStar: | |
| name = "InitAsyncStar"; | |
| break; | |
| case StubId::kInitSyncStar: | |
| name = "InitSyncStar"; | |
| break; | |
| case StubId::kFfiAsyncCallbackSend: | |
| name = "FfiAsyncCallbackSend"; | |
| break; | |
| } | |
| f->Printf("%s(", name); | |
| operand()->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void SuspendInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| const char* name = ""; | |
| switch (stub_id_) { | |
| case StubId::kAwait: | |
| name = "Await"; | |
| break; | |
| case StubId::kAwaitWithTypeCheck: | |
| name = "AwaitWithTypeCheck"; | |
| break; | |
| case StubId::kYieldAsyncStar: | |
| name = "YieldAsyncStar"; | |
| break; | |
| case StubId::kSuspendSyncStarAtStart: | |
| name = "SuspendSyncStarAtStart"; | |
| break; | |
| case StubId::kSuspendSyncStarAtYield: | |
| name = "SuspendSyncStarAtYield"; | |
| break; | |
| } | |
| f->Printf("%s(", name); | |
| Definition::PrintOperandsTo(f); | |
| f->AddString(")"); | |
| } | |
| void MoveArgumentInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| f->Printf("%s <- ", location().ToCString()); | |
| value()->PrintTo(f); | |
| } | |
| void GenericCheckBoundInstr::PrintOperandsTo(BaseTextBuffer* f) const { | |
| Definition::PrintOperandsTo(f); | |
| if (IsPhantom()) { | |
| f->AddString(", phantom"); | |
| } | |
| } | |
| void GotoInstr::PrintTo(BaseTextBuffer* f) const { | |
| if (HasParallelMove()) { | |
| parallel_move()->PrintTo(f); | |
| f->AddString(" "); | |
| } | |
| if (GetDeoptId() != DeoptId::kNone) { | |
| f->Printf("goto:%" Pd " B%" Pd "", GetDeoptId(), successor()->block_id()); | |
| } else { | |
| f->Printf("goto: B%" Pd "", successor()->block_id()); | |
| } | |
| } | |
| void IndirectGotoInstr::PrintTo(BaseTextBuffer* f) const { | |
| if (GetDeoptId() != DeoptId::kNone) { | |
| f->Printf("igoto:%" Pd "(", GetDeoptId()); | |
| } else { | |
| f->AddString("igoto:("); | |
| } | |
| InputAt(0)->PrintTo(f); | |
| f->AddString(")"); | |
| } | |
| void BranchInstr::PrintTo(BaseTextBuffer* f) const { | |
| f->Printf("%s ", DebugName()); | |
| f->AddString("if "); | |
| condition()->PrintTo(f); | |
| f->Printf(" goto (%" Pd ", %" Pd ")", true_successor()->block_id(), | |
| false_successor()->block_id()); | |
| } | |
| void ParallelMoveInstr::PrintTo(BaseTextBuffer* f) const { | |
| f->Printf("%s ", DebugName()); | |
| for (intptr_t i = 0; i < moves_.length(); i++) { | |
| if (i != 0) f->AddString(", "); | |
| moves_[i]->dest().PrintTo(f); | |
| f->AddString(" <- "); | |
| moves_[i]->src().PrintTo(f); | |
| } | |
| } | |
| void Utf8ScanInstr::PrintTo(BaseTextBuffer* f) const { | |
| Definition::PrintTo(f); | |
| f->Printf(" [%s]", scan_flags_field_.Name()); | |
| } | |
| void Environment::PrintTo(BaseTextBuffer* f) const { | |
| f->AddString(" env={ "); | |
| int arg_count = 0; | |
| for (intptr_t i = 0; i < values_.length(); ++i) { | |
| if (i > 0) f->AddString(", "); | |
| if (values_[i]->definition()->IsMoveArgument()) { | |
| f->Printf("a%d", arg_count++); | |
| } else { | |
| values_[i]->PrintTo(f); | |
| } | |
| if ((locations_ != nullptr) && !locations_[i].IsInvalid()) { | |
| f->AddString(" ["); | |
| locations_[i].PrintTo(f); | |
| f->AddString("]"); | |
| } | |
| } | |
| f->AddString(" }"); | |
| if (outer_ != nullptr) outer_->PrintTo(f); | |
| } | |
| const char* Environment::ToCString() const { | |
| char buffer[1024]; | |
| BufferFormatter bf(buffer, 1024); | |
| PrintTo(&bf); | |
| return Thread::Current()->zone()->MakeCopyOfString(buffer); | |
| } | |
| #else // defined(INCLUDE_IL_PRINTER) | |
| const char* Instruction::ToCString() const { | |
| return DebugName(); | |
| } | |
| void FlowGraphPrinter::PrintOneInstruction(Instruction* instr, | |
| bool print_locations) { | |
| UNREACHABLE(); | |
| } | |
| void FlowGraphPrinter::PrintTypeCheck(const ParsedFunction& parsed_function, | |
| TokenPosition token_pos, | |
| Value* value, | |
| const AbstractType& dst_type, | |
| const String& dst_name, | |
| bool eliminated) { | |
| UNREACHABLE(); | |
| } | |
| void FlowGraphPrinter::PrintBlock(BlockEntryInstr* block, | |
| bool print_locations) { | |
| UNREACHABLE(); | |
| } | |
| void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) { | |
| UNREACHABLE(); | |
| } | |
| void FlowGraphPrinter::PrintICData(const ICData& ic_data, | |
| intptr_t num_checks_to_print) { | |
| UNREACHABLE(); | |
| } | |
| bool FlowGraphPrinter::ShouldPrint( | |
| const Function& function, | |
| uint8_t** compiler_pass_filter /* = nullptr */) { | |
| return false; | |
| } | |
| #endif // defined(INCLUDE_IL_PRINTER) | |
| } // namespace dart |