// 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 "vm/compiler/api/print_filter.h" #include "vm/compiler/backend/il.h" #include "vm/compiler/backend/linearscan.h" #include "vm/compiler/backend/range_analysis.h" #include "vm/compiler/ffi/native_calling_convention.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."); DECLARE_FLAG(bool, trace_inlining_intervals); bool FlowGraphPrinter::ShouldPrint(const Function& function) { return compiler::PrintFilter::ShouldPrint(function); } void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) { LogBlock lb; 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(); 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)); instr->PrintTo(&f); if (FLAG_print_environments && (instr->env() != NULL)) { instr->env()->PrintTo(&f); } if (print_locations && (instr->HasLocs())) { instr->locs()->PrintTo(&f); } if (FlowGraphAllocator::HasLifetimePosition(instr)) { THR_Print("%3" Pd ": ", FlowGraphAllocator::GetLifetimePosition(instr)); } if (!instr->IsBlockEntry()) THR_Print(" "); THR_Print("%s", str); if (FLAG_trace_inlining_intervals) { THR_Print(" iid: %" Pd "", instr->inlining_id()); } } 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 != NULL && value->reaching_type_ != NULL) { 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", String::Handle(dst_type.Name()).ToCString(), 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 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()) { if (definition.HasPairRepresentation()) { f->Printf("(v%" Pd ", v%" Pd ")", definition.ssa_temp_index(), definition.ssa_temp_index() + 1); } else { 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) != NULL) 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_ != NULL) { f->AddString(" "); range_->PrintTo(f); } if (type_ != NULL) { 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) != NULL) { 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_ != NULL) && (reaching_type_ != definition()->type_)) { f->AddString(" "); reaching_type_->PrintTo(f); } } void ConstantInstr::PrintOperandsTo(BaseTextBuffer* f) const { const char* cstr = value().ToCString(); const char* new_line = strchr(cstr, '\n'); if (new_line == NULL) { f->Printf("#%s", cstr); } else { const intptr_t pos = new_line - cstr; char* buffer = Thread::Current()->zone()->Alloc(pos + 1); strncpy(buffer, cstr, pos); buffer[pos] = '\0'; f->Printf("#%s\\n...", buffer); } if (representation() != kNoRepresentation && representation() != kTagged) { f->Printf(" %s", RepresentationToCString(representation())); } } 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 == NULL) 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(value_)->ssa_temp_index()); if (offset_ != 0) f->Printf("%+" Pd64 "", offset_); break; case kNegativeInfinity: f->AddString("-inf"); break; case kPositiveInfinity: f->AddString("+inf"); 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() != NULL) { 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 AssertBooleanInstr::PrintOperandsTo(BaseTextBuffer* f) const { value()->PrintTo(f); } void ClosureCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { 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); } if (entry_kind() == Code::EntryKind::kUnchecked) { f->AddString(" using unchecked entrypoint"); } } 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 { left()->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 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 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 StoreInstanceFieldInstr::PrintOperandsTo(BaseTextBuffer* f) const { instance()->PrintTo(f); f->Printf(" . %s = ", slot().Name()); value()->PrintTo(f); // 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"); } } void IfThenElseInstr::PrintOperandsTo(BaseTextBuffer* f) const { comparison()->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 (calls_initializer()) { f->AddString(", CallsInitializer"); } } 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,", String::Handle(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 RelationalOpInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s, ", Token::Str(kind())); left()->PrintTo(f); f->AddString(", "); right()->PrintTo(f); } void AllocateObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s", String::Handle(cls().ScrubbedName()).ToCString()); for (intptr_t i = 0; i < InputCount(); ++i) { f->AddString(", "); InputAt(i)->PrintTo(f); } if (Identity().IsNotAliased()) { f->AddString(" "); } } void MaterializeObjectInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s", String::Handle(cls_.ScrubbedName()).ToCString()); 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(), slot().is_immutable() ? " {final}" : ""); if (calls_initializer()) { f->AddString(", CallsInitializer"); } } void LoadUntaggedInstr::PrintOperandsTo(BaseTextBuffer* f) const { object()->PrintTo(f); f->Printf(", %" Pd, offset()); } 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("%" Pd "", num_context_variables()); } void AllocateUninitializedContextInstr::PrintOperandsTo( BaseTextBuffer* f) const { f->Printf("%" Pd "", num_context_variables()); if (Identity().IsNotAliased()) { f->AddString(" "); } } void MathUnaryInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("'%s', ", MathUnaryInstr::KindToCString(kind())); value()->PrintTo(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 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; default: UNREACHABLE(); } value()->PrintTo(f); } static const char* 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(" "); } 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 CheckClassInstr::PrintOperandsTo(BaseTextBuffer* f) const { value()->PrintTo(f); PrintCidsHelper(f, cids_, FlowGraphPrinter::kPrintAll); if (IsNullCheck()) { f->AddString(" nullcheck"); } } void CheckConditionInstr::PrintOperandsTo(BaseTextBuffer* f) const { comparison()->PrintOperandsTo(f); } void InvokeMathCFunctionInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s, ", MethodRecognizer::KindToCString(recognized_kind_)); Definition::PrintOperandsTo(f); } void BlockEntryWithInitialDefs::PrintInitialDefinitionsTo( BaseTextBuffer* f) const { const GrowableArray& defns = initial_definitions_; if (defns.length() > 0) { f->AddString(" {"); for (intptr_t i = 0; i < defns.length(); ++i) { Definition* def = defns[i]; f->AddString("\n "); def->PrintTo(f); } f->AddString("\n}"); } } void GraphEntryInstr::PrintTo(BaseTextBuffer* f) const { f->Printf("B%" Pd "[graph]:%" Pd, block_id(), GetDeoptId()); BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f); } 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(")"); if (phis_ != NULL) { f->AddString(" {"); for (intptr_t i = 0; i < phis_->length(); ++i) { if ((*phis_)[i] == NULL) 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_ != NULL) { f->AddString(" {"); for (intptr_t i = 0; i < phis_->length(); ++i) { if ((*phis_)[i] == NULL) continue; f->AddString("\n "); (*phis_)[i]->PrintTo(f); } f->AddString("\n}"); } if (HasParallelMove()) { f->AddString(" "); parallel_move()->PrintTo(f); } } const char* RepresentationToCString(Representation rep) { switch (rep) { case kTagged: return "tagged"; case kUntagged: return "untagged"; case kUnboxedDouble: return "double"; case kUnboxedFloat: return "float"; case kUnboxedUint8: return "uint8"; case kUnboxedInt32: return "int32"; case kUnboxedUint32: return "uint32"; case kUnboxedInt64: return "int64"; case kUnboxedFloat32x4: return "float32x4"; case kUnboxedInt32x4: return "int32x4"; case kUnboxedFloat64x2: return "float64x2"; case kPairOfTagged: return "tagged-pair"; case kNoRepresentation: return "none"; case kNumRepresentations: UNREACHABLE(); } return "?"; } void PhiInstr::PrintTo(BaseTextBuffer* f) const { if (HasPairRepresentation()) { f->Printf("(v%" Pd ", v%" Pd ") <- phi(", ssa_temp_index(), ssa_temp_index() + 1); } else { f->Printf("v%" Pd " <- phi(", ssa_temp_index()); } for (intptr_t i = 0; i < inputs_.length(); ++i) { if (inputs_[i] != NULL) inputs_[i]->PrintTo(f); if (i < inputs_.length() - 1) f->AddString(", "); } f->AddString(")"); f->AddString(is_alive() ? " alive" : " dead"); if (range_ != NULL) { f->AddString(" "); range_->PrintTo(f); } if (representation() != kNoRepresentation && representation() != kTagged) { f->Printf(" %s", RepresentationToCString(representation())); } if (HasType()) { f->Printf(" %s", TypeAsCString()); } } void UnboxIntegerInstr::PrintOperandsTo(BaseTextBuffer* f) const { if (is_truncating()) { f->AddString("[tr], "); } Definition::PrintOperandsTo(f); } void IntConverterInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s->%s%s, ", RepresentationToCString(from()), RepresentationToCString(to()), is_truncating() ? "[tr]" : ""); Definition::PrintOperandsTo(f); } void BitCastInstr::PrintOperandsTo(BaseTextBuffer* f) const { Definition::PrintOperandsTo(f); f->Printf(" (%s -> %s)", RepresentationToCString(from()), RepresentationToCString(to())); } void ParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%" Pd, index()); } void SpecialParameterInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->Printf("%s", KindToCString(kind())); } const char* SpecialParameterInstr::ToCString() const { char buffer[1024]; BufferFormatter bf(buffer, 1024); PrintTo(&bf); return Thread::Current()->zone()->MakeCopyOfString(buffer); } 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()); } if (HasParallelMove()) { f->AddString(" "); parallel_move()->PrintTo(f); } } void OsrEntryInstr::PrintTo(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); } BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f); } void FunctionEntryInstr::PrintTo(BaseTextBuffer* f) const { f->Printf("B%" Pd "[function entry]:%" Pd, block_id(), GetDeoptId()); if (HasParallelMove()) { f->AddString("\n"); parallel_move()->PrintTo(f); } BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f); } void NativeEntryInstr::PrintTo(BaseTextBuffer* f) const { f->Printf("B%" Pd "[native function entry]:%" Pd, block_id(), GetDeoptId()); if (HasParallelMove()) { f->AddString("\n"); parallel_move()->PrintTo(f); } BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(f); } void ReturnInstr::PrintOperandsTo(BaseTextBuffer* f) const { Instruction::PrintOperandsTo(f); if (yield_index() != UntaggedPcDescriptors::kInvalidYieldIndex) { f->Printf(", yield_index = %" Pd "", yield_index()); } } void FfiCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { f->AddString(" pointer="); InputAt(TargetAddressIndex())->PrintTo(f); if (marshaller_.PassTypedData()) { f->AddString(", typed_data="); InputAt(TypedDataIndex())->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 EnterHandleScopeInstr::PrintOperandsTo(BaseTextBuffer* f) const { if (kind_ == Kind::kEnterHandleScope) { f->AddString(""); } else { f->AddString(""); } } void NativeReturnInstr::PrintOperandsTo(BaseTextBuffer* f) const { value()->PrintTo(f); 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 CatchBlockEntryInstr::PrintTo(BaseTextBuffer* f) const { f->Printf("B%" Pd "[target catch try_idx %" Pd " catch_try_idx %" Pd "]", block_id(), try_index(), catch_try_index()); if (HasParallelMove()) { f->AddString("\n"); parallel_move()->PrintTo(f); } BlockEntryWithInitialDefs::PrintInitialDefinitionsTo(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 StoreIndexedInstr::PrintOperandsTo(BaseTextBuffer* f) const { Instruction::PrintOperandsTo(f); if (!ShouldEmitStoreBarrier()) { f->AddString(", NoStoreBarrier"); } } void TailCallInstr::PrintOperandsTo(BaseTextBuffer* f) const { const char* name = ""; 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 PushArgumentInstr::PrintOperandsTo(BaseTextBuffer* f) const { value()->PrintTo(f); } 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 "); comparison()->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()->IsPushArgument()) { f->Printf("a%d", arg_count++); } else { values_[i]->PrintTo(f); } if ((locations_ != NULL) && !locations_[i].IsInvalid()) { f->AddString(" ["); locations_[i].PrintTo(f); f->AddString("]"); } } f->AddString(" }"); if (outer_ != NULL) 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) { return false; } #endif // defined(INCLUDE_IL_PRINTER) } // namespace dart