d9685d1c4c
These checks are now performed in the dynamic closure call dispatcher.
To avoid having to create type argument vectors (TAVs) for default type
arguments at runtime, we cache a compile-time created TAV in the
ClosureData for the closure function which is retrieved by the
dispatcher when needed.
We also keep an associated packed field of information that can also be
determined at compile time:
* Whether the cached TAV needs instantiation or can share its
instantiator or function type arguments.
* The number of parent type parameters.
The former allows the generated IL to keep the invariant that the
InstantiateTypeArguments instruction (and the runtime entry it calls) is
only used for uninstantiated TAVs.
Also changes the destination name to an Value input for the
AssertSubtype instruction and adds handling for non-constant types and
names in that instruction's backend.
Additional changes:
* Adds new slots for ClosureData, Function, and TypeArguments.
* Adds a new kUnboxedUint8 representation (needed for
TypeParameterLayout::flags_, which is of type uint8_t).
* Extends LoadField to handle uint8_t unboxed native fields.
* Adds BoxUint8 for boxing unboxed uint8_t values.
Code size impact on Flutter gallery in release mode:
* arm7: total +0.08%, vmisolate: +0.56%, isolate: +0.42%,
readonly: -0.04%, instructions: -0.03%
* arm8: total +0.12%, vmisolate: +0.56%, isolate: +0.42%,
readonly: -0.002%, instructions: +0.03%
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-nnbd-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-nnbd-linux-release-simarm64-try,vm-kernel-precomp-linux-release-simarm-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-precomp-nnbd-linux-release-simarm64-try
Bug: https://github.com/dart-lang/sdk/issues/40813
Change-Id: I5a7de27a17e3119e27752bd0d10e1c6bc1b52a16
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/158844
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
1267 lines
38 KiB
C++
1267 lines
38 KiB
C++
// 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(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
|
|
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_.IsInvokeFieldDispatcher() ||
|
|
function_.IsNoSuchMethodDispatcher()) {
|
|
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->raw();
|
|
if (i > 0) {
|
|
f->AddString(" | ");
|
|
}
|
|
if (range.IsSingleCid()) {
|
|
const Class& cls =
|
|
Class::Handle(Isolate::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(Isolate::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(Isolate::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(Isolate::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);
|
|
}
|
|
|
|
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<char>(pos + 1);
|
|
strncpy(buffer, cstr, pos);
|
|
buffer[pos] = '\0';
|
|
f->Printf("#%s\\n...", 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 == 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<Definition*>(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(" <not-aliased>");
|
|
}
|
|
}
|
|
|
|
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(" <not-aliased>");
|
|
}
|
|
}
|
|
|
|
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 CheckedSmiOpInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
|
f->Printf("%s", Token::Str(op_kind()));
|
|
f->AddString(", ");
|
|
left()->PrintTo(f);
|
|
f->AddString(", ");
|
|
right()->PrintTo(f);
|
|
}
|
|
|
|
void CheckedSmiComparisonInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
|
f->Printf("%s", Token::Str(kind()));
|
|
f->AddString(", ");
|
|
left()->PrintTo(f);
|
|
f->AddString(", ");
|
|
right()->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("<non-smi> ");
|
|
}
|
|
object()->PrintTo(f);
|
|
}
|
|
|
|
void CheckClassIdInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
|
value()->PrintTo(f);
|
|
|
|
const Class& cls =
|
|
Class::Handle(Isolate::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(Isolate::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<Definition*>& 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() != PcDescriptorsLayout::kInvalidYieldIndex) {
|
|
f->Printf(", yield_index = %" Pd "", yield_index());
|
|
}
|
|
}
|
|
|
|
void FfiCallInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
|
f->AddString(" pointer=");
|
|
InputAt(TargetAddressIndex())->PrintTo(f);
|
|
for (intptr_t i = 0, n = InputCount(); i < n - 1; ++i) {
|
|
f->AddString(", ");
|
|
InputAt(i)->PrintTo(f);
|
|
f->AddString(" (@");
|
|
marshaller_.Location(i).PrintTo(f);
|
|
f->AddString(")");
|
|
}
|
|
}
|
|
|
|
void EnterHandleScopeInstr::PrintOperandsTo(BaseTextBuffer* f) const {
|
|
if (kind_ == Kind::kEnterHandleScope) {
|
|
f->AddString("<enter handle scope>");
|
|
} else {
|
|
f->AddString("<get top api scope>");
|
|
}
|
|
}
|
|
|
|
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(index_).PrintTo(f);
|
|
f->AddString(" at ");
|
|
// Where the arguments are when pushed on the stack.
|
|
marshaller_.NativeLocationOfNativeParameter(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 = "<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.raw()))
|
|
.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(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
|
|
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(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
|
|
|
} // namespace dart
|