blob: 31fd469c2406d85387f3a0508522e3cf32bf2075 [file] [log] [blame]
// Copyright (c) 2019, 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_serializer.h"
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
#include "vm/compiler/backend/range_analysis.h"
#include "vm/compiler/method_recognizer.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/zone_text_buffer.h"
namespace dart {
DEFINE_FLAG(bool,
serialize_flow_graph_types,
true,
"Serialize inferred type information in flow graphs");
DEFINE_FLAG(bool,
verbose_flow_graph_serialization,
false,
"Serialize extra information useful for debugging");
DEFINE_FLAG(bool,
pretty_print_serialization,
false,
"Format serialized output nicely");
DECLARE_FLAG(bool, populate_llvm_constant_pool);
const char* const FlowGraphSerializer::initial_indent = "";
FlowGraphSerializer::FlowGraphSerializer(Zone* zone,
const FlowGraph* flow_graph)
: flow_graph_(ASSERT_NOTNULL(flow_graph)),
zone_(zone),
object_store_(flow_graph->thread()->isolate_group()->object_store()),
open_recursive_types_(zone_),
llvm_constants_(
GrowableObjectArray::Handle(zone_,
object_store_->llvm_constant_pool())),
llvm_functions_(
GrowableObjectArray::Handle(zone_,
object_store_->llvm_function_pool())),
llvm_constant_map_(zone_, object_store_->llvm_constant_hash_table()),
llvm_index_(Smi::Handle(zone_)),
tmp_string_(String::Handle(zone_)),
array_type_args_((TypeArguments::Handle(zone_))),
closure_context_(Context::Handle(zone_)),
closure_function_(Function::Handle(zone_)),
closure_type_args_(TypeArguments::Handle(zone_)),
code_owner_(Object::Handle(zone_)),
context_parent_(Context::Handle(zone_)),
context_elem_(Object::Handle(zone_)),
function_type_args_(TypeArguments::Handle(zone_)),
ic_data_target_(Function::Handle(zone_)),
ic_data_type_(AbstractType::Handle(zone_)),
instance_field_(Field::Handle(zone_)),
instance_type_args_(TypeArguments::Handle(zone_)),
serialize_library_(Library::Handle(zone_)),
serialize_owner_(Class::Handle(zone_)),
serialize_parent_(Function::Handle(zone_)),
type_arguments_elem_(AbstractType::Handle(zone_)),
type_class_(Class::Handle(zone_)),
type_signature_(FunctionType::Handle(zone_)),
type_ref_type_(AbstractType::Handle(zone_)) {
// Double-check that the zone in the flow graph is a parent of the
// zone we'll be using for serialization.
ASSERT(flow_graph->zone()->ContainsNestedZone(zone));
}
FlowGraphSerializer::~FlowGraphSerializer() {
object_store_->set_llvm_constant_hash_table(llvm_constant_map_.Release());
}
void FlowGraphSerializer::SerializeToBuffer(Zone* zone,
const FlowGraph* flow_graph,
BaseTextBuffer* buffer) {
ASSERT(buffer != nullptr);
auto const sexp = SerializeToSExp(zone, flow_graph);
if (FLAG_pretty_print_serialization) {
sexp->SerializeTo(zone, buffer, initial_indent);
} else {
sexp->SerializeToLine(buffer);
}
buffer->AddString("\n\n");
}
SExpression* FlowGraphSerializer::SerializeToSExp(Zone* zone,
const FlowGraph* flow_graph) {
FlowGraphSerializer serializer(zone, flow_graph);
return serializer.FlowGraphToSExp();
}
#define KIND_STR(name) #name,
static const char* block_entry_kind_tags[FlowGraphSerializer::kNumEntryKinds] =
{FOR_EACH_BLOCK_ENTRY_KIND(KIND_STR)};
#undef KIND_STR
FlowGraphSerializer::BlockEntryKind FlowGraphSerializer::BlockEntryTagToKind(
SExpSymbol* tag) {
if (tag == nullptr) return kTarget;
auto const str = tag->value();
for (intptr_t i = 0; i < kNumEntryKinds; i++) {
auto const current = block_entry_kind_tags[i];
if (strcmp(str, current) == 0) return static_cast<BlockEntryKind>(i);
}
return kInvalid;
}
void FlowGraphSerializer::AddBool(SExpList* sexp, bool b) {
sexp->Add(new (zone()) SExpBool(b));
}
void FlowGraphSerializer::AddInteger(SExpList* sexp, intptr_t i) {
sexp->Add(new (zone()) SExpInteger(i));
}
void FlowGraphSerializer::AddString(SExpList* sexp, const char* cstr) {
sexp->Add(new (zone()) SExpString(cstr));
}
void FlowGraphSerializer::AddSymbol(SExpList* sexp, const char* cstr) {
sexp->Add(new (zone()) SExpSymbol(cstr));
}
void FlowGraphSerializer::AddExtraBool(SExpList* sexp,
const char* label,
bool b) {
sexp->AddExtra(label, new (zone()) SExpBool(b));
}
void FlowGraphSerializer::AddExtraInteger(SExpList* sexp,
const char* label,
intptr_t i) {
sexp->AddExtra(label, new (zone()) SExpInteger(i));
}
void FlowGraphSerializer::AddExtraString(SExpList* sexp,
const char* label,
const char* cstr) {
sexp->AddExtra(label, new (zone()) SExpString(cstr));
}
void FlowGraphSerializer::AddExtraSymbol(SExpList* sexp,
const char* label,
const char* cstr) {
sexp->AddExtra(label, new (zone()) SExpSymbol(cstr));
}
SExpression* FlowGraphSerializer::BlockIdToSExp(intptr_t block_id) {
return new (zone()) SExpSymbol(OS::SCreate(zone(), "B%" Pd "", block_id));
}
void FlowGraphSerializer::SerializeCanonicalName(BaseTextBuffer* b,
const Object& obj) {
ASSERT(!obj.IsNull());
if (obj.IsFunction()) {
const auto& function = Function::Cast(obj);
tmp_string_ = function.name();
// We only want private keys removed, no other changes.
tmp_string_ = String::RemovePrivateKey(tmp_string_);
const char* function_name = tmp_string_.ToCString();
// If this function is an inner closure then the parent points to its
// containing function, which will also be part of the canonical name.
//
// We retrieve the owner before retrieving the parent function, as the
// inner closure chain may be arbitrarily deep and serialize_parent_ is
// passed in on recursive calls. When it is, then changing serialize_parent_
// to the parent function also changes the contents of obj and thus we'd
// no longer be able to retrieve the child function or its owner.
//
// This does mean that serialize_owner_ gets overwritten for each recursive
// call until we reach the end of the chain, but we only use its contents at
// the end of the chain anyway.
serialize_owner_ = function.Owner();
serialize_parent_ = function.parent_function();
if (!serialize_parent_.IsNull()) {
SerializeCanonicalName(b, serialize_parent_);
} else {
ASSERT(!serialize_owner_.IsNull());
SerializeCanonicalName(b, serialize_owner_);
}
b->Printf(":%s", function_name);
} else if (obj.IsClass()) {
const auto& cls = Class::Cast(obj);
tmp_string_ = cls.ScrubbedName();
const char* class_name = tmp_string_.ToCString();
serialize_library_ = cls.library();
if (!serialize_library_.IsNull()) {
SerializeCanonicalName(b, serialize_library_);
}
b->Printf(":%s", class_name);
} else if (obj.IsLibrary()) {
const Library& lib = Library::Cast(obj);
tmp_string_ = lib.url();
const char* lib_name = tmp_string_.ToCString();
if (lib_name[0] == '\0') return;
b->AddString(lib_name);
} else if (obj.IsField()) {
const auto& field = Field::Cast(obj);
tmp_string_ = field.UserVisibleName();
const char* field_name = tmp_string_.ToCString();
serialize_owner_ = field.Owner();
ASSERT(!serialize_owner_.IsNull());
SerializeCanonicalName(b, serialize_owner_);
b->Printf(".%s", field_name);
} else {
UNREACHABLE();
}
}
SExpression* FlowGraphSerializer::CanonicalNameToSExp(const Object& obj) {
ASSERT(!obj.IsNull());
ZoneTextBuffer b(zone_, 100);
SerializeCanonicalName(&b, obj);
return new (zone()) SExpSymbol(b.buffer());
}
SExpSymbol* FlowGraphSerializer::BlockEntryKindToTag(BlockEntryKind k) {
ASSERT(k >= 0 && k < kNumEntryKinds);
return new (zone()) SExpSymbol(block_entry_kind_tags[k]);
}
#define KIND_TAG(name) block_entry_kind_tags[k##name]
SExpSymbol* FlowGraphSerializer::BlockEntryTag(const BlockEntryInstr* entry) {
if (entry == nullptr) return nullptr;
if (entry->IsGraphEntry()) {
return BlockEntryKindToTag(kGraph);
}
if (entry->IsOsrEntry()) {
return BlockEntryKindToTag(kOSR);
}
if (entry->IsCatchBlockEntry()) {
return BlockEntryKindToTag(kCatch);
}
if (entry->IsIndirectEntry()) {
return BlockEntryKindToTag(kIndirect);
}
if (entry->IsFunctionEntry()) {
if (entry == flow_graph()->graph_entry()->normal_entry()) {
return BlockEntryKindToTag(kNormal);
}
if (entry == flow_graph()->graph_entry()->unchecked_entry()) {
return BlockEntryKindToTag(kUnchecked);
}
}
if (entry->IsJoinEntry()) {
return BlockEntryKindToTag(kJoin);
}
return nullptr;
}
#undef KIND_TAG
SExpression* FlowGraphSerializer::FunctionEntryToSExp(
const BlockEntryInstr* entry) {
if (entry == nullptr) return nullptr;
auto sexp = new (zone()) SExpList(zone());
sexp->Add(BlockEntryTag(entry));
sexp->Add(BlockIdToSExp(entry->block_id()));
if (auto const with_defs = entry->AsBlockEntryWithInitialDefs()) {
auto const initial_defs = with_defs->initial_definitions();
for (intptr_t i = 0; i < initial_defs->length(); i++) {
sexp->Add(initial_defs->At(i)->ToSExpression(this));
}
}
// Also include the extra info here, to avoid having to find the
// corresponding block to get it.
entry->BlockEntryInstr::AddExtraInfoToSExpression(sexp, this);
return sexp;
}
SExpression* FlowGraphSerializer::EntriesToSExp(const GraphEntryInstr* start) {
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Entries");
if (auto const normal = FunctionEntryToSExp(start->normal_entry())) {
sexp->Add(normal);
}
if (auto const unchecked = FunctionEntryToSExp(start->unchecked_entry())) {
sexp->Add(unchecked);
}
if (auto const osr = FunctionEntryToSExp(start->osr_entry())) {
sexp->Add(osr);
}
for (intptr_t i = 0; i < start->catch_entries().length(); i++) {
sexp->Add(FunctionEntryToSExp(start->catch_entries().At(i)));
}
for (intptr_t i = 0; i < start->indirect_entries().length(); i++) {
sexp->Add(FunctionEntryToSExp(start->indirect_entries().At(i)));
}
return sexp;
}
SExpression* FlowGraphSerializer::FlowGraphToSExp() {
auto const start = flow_graph()->graph_entry();
auto const sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "FlowGraph");
sexp->Add(CanonicalNameToSExp(flow_graph()->function()));
AddExtraInteger(sexp, "deopt_id", start->deopt_id());
if (start->env() != nullptr) {
sexp->AddExtra("env", start->env()->ToSExpression(this));
}
if (start->IsCompiledForOsr()) {
AddExtraInteger(sexp, "osr_id", start->osr_id());
}
if (auto const constants = ConstantPoolToSExp(start)) {
sexp->Add(constants);
}
sexp->Add(EntriesToSExp(start));
auto& block_order = flow_graph()->reverse_postorder();
// Skip the first block, which will be the graph entry block (B0). We
// output all its information as part of the function expression, so it'll
// just show up as an empty block here.
ASSERT(block_order[0]->IsGraphEntry());
for (intptr_t i = 1; i < block_order.length(); ++i) {
sexp->Add(block_order[i]->ToSExpression(this));
}
if (FLAG_populate_llvm_constant_pool) {
auto const new_index = llvm_functions_.Length();
llvm_functions_.Add(flow_graph_->function());
AddExtraInteger(sexp, "llvm_index", new_index);
}
return sexp;
}
SExpression* FlowGraphSerializer::UseToSExp(const Definition* definition) {
ASSERT(definition != nullptr);
ASSERT(definition->HasSSATemp() || definition->HasTemp());
if (definition->HasSSATemp()) {
const intptr_t temp_index = definition->ssa_temp_index();
const auto name_cstr = OS::SCreate(zone(), "v%" Pd "", temp_index);
if (definition->HasPairRepresentation()) {
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, name_cstr);
AddSymbol(sexp, OS::SCreate(zone(), "v%" Pd "", temp_index + 1));
return sexp;
} else {
return new (zone()) SExpSymbol(name_cstr);
}
} else if (definition->HasTemp()) {
const intptr_t temp_index = definition->temp_index();
return new (zone()) SExpSymbol(OS::SCreate(zone(), "t%" Pd "", temp_index));
}
UNREACHABLE();
}
SExpression* FlowGraphSerializer::ClassToSExp(const Class& cls) {
if (cls.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Class");
AddInteger(sexp, cls.id());
if (FLAG_verbose_flow_graph_serialization) {
sexp->AddExtra("name", CanonicalNameToSExp(cls));
// Currently, AbstractTypeToSExp assumes that serializing a class cannot
// re-enter it. If we make that possible by serializing parts of a class
// that can contain AbstractTypes, especially types that are not type
// parameters or type references, fix AbstractTypeToSExp appropriately.
}
return sexp;
}
static bool ShouldSerializeType(CompileType* type) {
return (FLAG_verbose_flow_graph_serialization ||
FLAG_serialize_flow_graph_types) &&
type != nullptr;
}
SExpression* FlowGraphSerializer::FieldToSExp(const Field& field) {
if (field.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Field");
sexp->Add(CanonicalNameToSExp(field));
CompileType t(field.is_nullable(), field.guarded_cid(), nullptr);
if (ShouldSerializeType(&t)) {
sexp->AddExtra("type", t.ToSExpression(this));
}
return sexp;
}
SExpression* FlowGraphSerializer::AbstractTypeToSExp(const AbstractType& t) {
if (t.IsNull()) return nullptr;
ASSERT(t.IsFinalized());
auto sexp = new (zone()) SExpList(zone());
if (t.IsTypeParameter()) {
const auto& param = TypeParameter::Cast(t);
AddSymbol(sexp, "TypeParameter");
AddExtraInteger(sexp, "cid", param.parameterized_class_id());
AddExtraInteger(sexp, "base", param.base());
AddExtraInteger(sexp, "index", param.index());
tmp_string_ = param.name();
AddSymbol(sexp, tmp_string_.ToCString());
// TODO(regis): bound, default argument, flags, nullability, hash.
return sexp;
}
if (t.IsTypeRef()) {
const auto& ref = TypeRef::Cast(t);
AddSymbol(sexp, "TypeRef");
type_ref_type_ = ref.type();
auto const hash = type_ref_type_.Hash();
// Check to see if this is a TypeRef to a type we're currently serializing.
// If it is not, then we need to serialize the underlying type, as it
// otherwise won't be available when deserializing.
auto const open_type = open_recursive_types_.LookupValue(hash);
if (open_type == nullptr) {
// Allocate a new handle as we may re-enter the TypeRef branch.
auto& type = AbstractType::Handle(zone(), ref.type());
sexp->Add(AbstractTypeToSExp(type));
// If we serialized the referrent, then we don't need this information,
// but it may be useful for debugging so add it in verbose mode.
if (FLAG_verbose_flow_graph_serialization) {
AddExtraInteger(sexp, "hash", hash);
}
} else {
// Make sure we didn't have a hash collision.
ASSERT(open_type->Equals(type_ref_type_));
AddExtraInteger(sexp, "hash", hash);
}
if (FLAG_verbose_flow_graph_serialization) {
AddExtraString(sexp, "type", type_ref_type_.ToCString());
}
return sexp;
}
// We want to check for the type being recursive before we may serialize
// any sub-parts that include possible TypeRefs to this type.
const bool is_recursive = t.IsRecursive();
intptr_t hash = 0;
if (is_recursive) {
hash = t.Hash();
AddExtraInteger(sexp, "hash", hash);
open_recursive_types_.Insert(hash, &t);
}
if (t.IsFunctionType()) {
const auto& sig = FunctionType::Handle(zone(), FunctionType::Cast(t).ptr());
AddSymbol(sexp, "FunctionType");
function_type_args_ = sig.type_parameters();
if (auto const ta_sexp = NonEmptyTypeArgumentsToSExp(function_type_args_)) {
sexp->AddExtra("type_params", ta_sexp);
}
auto& type = AbstractType::Handle(zone(), sig.result_type());
sexp->AddExtra("result_type", AbstractTypeToSExp(type));
auto& parameter_types = Array::Handle(zone(), sig.parameter_types());
sexp->AddExtra("parameter_types", ArrayToSExp(parameter_types));
auto& parameter_names = Array::Handle(zone(), sig.parameter_names());
sexp->AddExtra("parameter_names", ArrayToSExp(parameter_names));
AddExtraInteger(sexp, "packed_fields", sig.packed_fields());
// If we were parsing a recursive type, we're now done building it, so
// remove it from the open recursive types.
if (is_recursive) open_recursive_types_.Remove(hash);
return sexp;
}
ASSERT(t.IsType());
AddSymbol(sexp, "Type");
const auto& type = Type::Cast(t);
if (type.HasTypeClass()) {
type_class_ = type.type_class();
// This avoids re-entry as long as serializing a class doesn't involve
// serializing concrete (non-parameter, non-reference) types.
sexp->Add(DartValueToSExp(type_class_));
} else {
// TODO(dartbug.com/36882): Actually structure non-class types instead of
// just printing out this version.
AddExtraString(sexp, "name", type.ToCString());
}
// Since type arguments may themselves be instantiations of generic
// types, we may call back into this function in the middle of printing
// the TypeArguments and so we must allocate a fresh handle here.
const auto& args = TypeArguments::Handle(zone(), type.arguments());
if (auto const args_sexp = NonEmptyTypeArgumentsToSExp(args)) {
sexp->AddExtra("type_args", args_sexp);
}
// If we were parsing a recursive type, we're now done building it, so
// remove it from the open recursive types.
if (is_recursive) open_recursive_types_.Remove(hash);
return sexp;
}
SExpression* FlowGraphSerializer::CodeToSExp(const Code& code) {
if (code.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Code");
if (code.IsStubCode()) {
AddSymbol(sexp, StubCode::NameOfStub(code.EntryPoint()));
if (FLAG_verbose_flow_graph_serialization) {
AddExtraSymbol(sexp, "kind", "stub");
}
return sexp;
}
code_owner_ = code.owner();
if (!code_owner_.IsNull() && FLAG_verbose_flow_graph_serialization) {
if (code_owner_.IsClass()) {
AddExtraSymbol(sexp, "kind", "allocate");
} else if (code_owner_.IsAbstractType()) {
AddExtraSymbol(sexp, "kind", "type_test");
} else {
ASSERT(code_owner_.IsFunction());
AddExtraSymbol(sexp, "kind", "function");
}
}
sexp->Add(DartValueToSExp(code_owner_));
return sexp;
}
SExpression* FlowGraphSerializer::TypeArgumentsToSExp(const TypeArguments& ta) {
if (ta.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "TypeArguments");
for (intptr_t i = 0; i < ta.Length(); i++) {
type_arguments_elem_ = ta.TypeAt(i);
sexp->Add(DartValueToSExp(type_arguments_elem_));
}
if (FLAG_verbose_flow_graph_serialization && ta.IsRecursive()) {
AddExtraInteger(sexp, "hash", ta.Hash());
}
return sexp;
}
SExpression* FlowGraphSerializer::InstanceToSExp(const Instance& inst) {
if (inst.IsNull()) return nullptr;
// Since InstanceToSExp may use ObjectToSExp (via DartValueToSExp) for field
// values that aren't entries in the constant pool, and ObjectToSExp may
// re-enter InstanceToSExp, allocate fresh handles here for the argument to
// DartValueToSExp and other handles that are live across the call.
const auto& instance_class = Class::Handle(zone(), inst.clazz());
const auto& instance_fields_array =
Array::Handle(zone(), instance_class.fields());
auto& instance_field_value = Object::Handle(zone());
auto const sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Instance");
AddInteger(sexp, instance_class.id());
auto const fields = new (zone()) SExpList(zone());
AddSymbol(fields, "Fields");
for (intptr_t i = 0; i < instance_fields_array.Length(); i++) {
instance_field_ = Field::RawCast(instance_fields_array.At(i));
// We don't need to serialize static fields, since they're shared by
// all instances.
if (instance_field_.is_static()) continue;
// We should only be getting const instances, which means that we
// should only see final instance fields.
ASSERT(instance_field_.is_final());
tmp_string_ = instance_field_.UserVisibleName();
auto const label = tmp_string_.ToCString();
instance_field_value = inst.GetField(instance_field_);
fields->AddExtra(label, DartValueToSExp(instance_field_value));
}
if (fields->ExtraLength() != 0 || FLAG_verbose_flow_graph_serialization) {
sexp->Add(fields);
}
if (instance_class.IsGeneric()) {
instance_type_args_ = inst.GetTypeArguments();
if (auto const args = NonEmptyTypeArgumentsToSExp(instance_type_args_)) {
sexp->AddExtra("type_args", args);
}
}
if (FLAG_verbose_flow_graph_serialization) {
AddExtraInteger(sexp, "size", inst.InstanceSize());
// We know the following won't call back into InstanceToSExp because we're
// providing it a class.
if (auto const cls = DartValueToSExp(instance_class)) {
sexp->AddExtra("class", cls);
}
}
return sexp;
}
SExpression* FlowGraphSerializer::FunctionToSExp(const Function& func) {
if (func.IsNull()) return nullptr;
auto const sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Function");
sexp->Add(CanonicalNameToSExp(func));
if (func.IsRecognized()) {
AddExtraSymbol(sexp, "recognized",
MethodRecognizer::KindToCString(func.recognized_kind()));
}
if (func.is_native()) {
tmp_string_ = func.native_name();
if (!tmp_string_.IsNull()) {
AddExtraSymbol(sexp, "native_name", tmp_string_.ToCString());
}
}
if (func.kind() != UntaggedFunction::Kind::kRegularFunction ||
FLAG_verbose_flow_graph_serialization) {
AddExtraSymbol(sexp, "kind", UntaggedFunction::KindToCString(func.kind()));
}
function_type_args_ = func.type_parameters();
if (auto const ta_sexp = NonEmptyTypeArgumentsToSExp(function_type_args_)) {
sexp->AddExtra("type_args", ta_sexp);
}
return sexp;
}
SExpression* FlowGraphSerializer::ImmutableListToSExp(const Array& arr) {
if (arr.IsNull()) return nullptr;
// We should only be getting immutable lists when serializing Dart values
// in flow graphs.
ASSERT(arr.IsImmutable());
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "ImmutableList");
// We allocate a new Object handle to use for the calls to DartValueToSExp
// in case any Array elements contain non-constant-pool, non-empty Arrays.
auto& array_elem = Object::Handle(zone());
for (intptr_t i = 0; i < arr.Length(); i++) {
array_elem = arr.At(i);
sexp->Add(DartValueToSExp(array_elem));
}
array_type_args_ = arr.GetTypeArguments();
if (auto const type_args_sexp = TypeArgumentsToSExp(array_type_args_)) {
sexp->AddExtra("type_args", type_args_sexp);
}
return sexp;
}
SExpression* FlowGraphSerializer::ArrayToSExp(const Array& arr) {
if (arr.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Array");
auto& array_elem = Object::Handle(zone());
for (intptr_t i = 0; i < arr.Length(); i++) {
array_elem = arr.At(i);
sexp->Add(DartValueToSExp(array_elem));
}
return sexp;
}
SExpression* FlowGraphSerializer::ClosureToSExp(const Closure& c) {
if (c.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Closure");
closure_function_ = c.function();
if (auto const func = FunctionToSExp(closure_function_)) {
sexp->Add(func);
}
closure_context_ = c.context();
if (auto const context = ContextToSExp(closure_context_)) {
sexp->AddExtra("context", context);
}
closure_type_args_ = c.function_type_arguments();
if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) {
sexp->AddExtra("func_type_args", type_args);
}
closure_type_args_ = c.instantiator_type_arguments();
if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) {
sexp->AddExtra("inst_type_args", type_args);
}
closure_type_args_ = c.delayed_type_arguments();
if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) {
sexp->AddExtra("delayed_type_args", type_args);
}
return sexp;
}
SExpression* FlowGraphSerializer::ContextToSExp(const Context& c) {
if (c.IsNull()) return nullptr;
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Context");
for (intptr_t i = 0; i < c.num_variables(); i++) {
context_elem_ = c.At(i);
auto const elem_sexp = DartValueToSExp(context_elem_);
if (elem_sexp == nullptr) return nullptr;
sexp->Add(elem_sexp);
}
context_parent_ = c.parent();
if (auto const parent_sexp = ContextToSExp(context_parent_)) {
sexp->AddExtra("parent", parent_sexp);
}
return sexp;
}
SExpression* FlowGraphSerializer::ObjectToSExp(const Object& dartval) {
if (dartval.IsNull()) {
return new (zone()) SExpSymbol("null");
}
if (dartval.ptr() == Object::sentinel().ptr()) {
return new (zone()) SExpSymbol("sentinel");
}
if (dartval.IsString()) {
return new (zone()) SExpString(dartval.ToCString());
}
if (dartval.IsSmi()) {
return new (zone()) SExpInteger(Smi::Cast(dartval).Value());
}
if (dartval.IsMint()) {
return new (zone()) SExpInteger(Mint::Cast(dartval).value());
}
if (dartval.IsBool()) {
return new (zone()) SExpBool(Bool::Cast(dartval).value());
}
if (dartval.IsDouble()) {
return new (zone()) SExpDouble(Double::Cast(dartval).value());
}
if (dartval.IsField()) {
return FieldToSExp(Field::Cast(dartval));
}
if (dartval.IsClass()) {
return ClassToSExp(Class::Cast(dartval));
}
if (dartval.IsTypeArguments()) {
return TypeArgumentsToSExp(TypeArguments::Cast(dartval));
}
if (dartval.IsCode()) {
return CodeToSExp(Code::Cast(dartval));
}
if (dartval.IsArray()) {
return ImmutableListToSExp(Array::Cast(dartval));
}
if (dartval.IsFunction()) {
return FunctionToSExp(Function::Cast(dartval));
}
if (dartval.IsClosure()) {
return ClosureToSExp(Closure::Cast(dartval));
}
if (dartval.IsAbstractType()) {
return AbstractTypeToSExp(AbstractType::Cast(dartval));
}
ASSERT(dartval.IsInstance());
return InstanceToSExp(Instance::Cast(dartval));
}
SExpression* FlowGraphSerializer::DartValueToSExp(const Object& obj) {
if (auto const def = flow_graph()->GetExistingConstant(obj)) {
ASSERT(def->IsDefinition());
return UseToSExp(def->AsDefinition());
}
return ObjectToSExp(obj);
}
SExpression* FlowGraphSerializer::NonEmptyTypeArgumentsToSExp(
const TypeArguments& ta) {
if (ta.IsNull() || ta.Length() == 0) return nullptr;
return DartValueToSExp(ta);
}
SExpression* FlowGraphSerializer::ConstantPoolToSExp(
const GraphEntryInstr* start) {
auto const initial_defs = start->initial_definitions();
if (initial_defs == nullptr || initial_defs->is_empty()) return nullptr;
auto constant_list = new (zone()) SExpList(zone());
AddSymbol(constant_list, "Constants");
for (intptr_t i = 0; i < initial_defs->length(); i++) {
ASSERT(initial_defs->At(i)->IsConstant());
auto const definition = initial_defs->At(i)->AsDefinition();
auto elem = new (zone()) SExpList(zone());
AddSymbol(elem, "def");
elem->Add(UseToSExp(definition));
// Use ObjectToSExp here, not DartValueToSExp!
const auto& value = definition->AsConstant()->value();
elem->Add(ObjectToSExp(value));
AddDefinitionExtraInfoToSExp(definition, elem);
// Only add constants to the LLVM constant pool that are actually used in
// the flow graph.
if (FLAG_populate_llvm_constant_pool && definition->HasUses()) {
auto const pool_len = llvm_constants_.Length();
llvm_index_ = Smi::New(pool_len);
llvm_index_ ^= llvm_constant_map_.InsertOrGetValue(value, llvm_index_);
if (llvm_index_.Value() == pool_len) {
llvm_constants_.Add(value);
}
AddExtraInteger(elem, "llvm_index", llvm_index_.Value());
}
constant_list->Add(elem);
}
return constant_list;
}
SExpression* Instruction::ToSExpression(FlowGraphSerializer* s) const {
auto sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, DebugName());
AddOperandsToSExpression(sexp, s);
AddExtraInfoToSExpression(sexp, s);
return sexp;
}
SExpression* BlockEntryInstr::ToSExpression(FlowGraphSerializer* s) const {
auto sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, "Block");
sexp->Add(s->BlockIdToSExp(block_id()));
AddOperandsToSExpression(sexp, s);
AddExtraInfoToSExpression(sexp, s);
return sexp;
}
void BlockEntryInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (try_index() != kInvalidTryIndex) {
s->AddExtraInteger(sexp, "try_index", try_index());
}
if (auto const entry_tag = s->BlockEntryTag(this)) {
sexp->AddExtra("block_type", entry_tag);
}
if (FLAG_verbose_flow_graph_serialization) {
if (PredecessorCount() > 0) {
auto const preds = new (s->zone()) SExpList(s->zone());
for (intptr_t i = 0; i < PredecessorCount(); i++) {
preds->Add(s->BlockIdToSExp(PredecessorAt(i)->block_id()));
}
sexp->AddExtra("predecessors", preds);
}
}
}
void BlockEntryInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
for (const auto* inst = next_; inst != nullptr; inst = inst->next_) {
sexp->Add(inst->ToSExpression(s));
}
}
void JoinEntryInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
if (auto phi_list = phis()) {
for (intptr_t i = 0; i < phi_list->length(); i++) {
sexp->Add(phi_list->At(i)->ToSExpression(s));
}
}
BlockEntryInstr::AddOperandsToSExpression(sexp, s);
}
void Instruction::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
for (intptr_t i = 0; i < InputCount(); ++i) {
if (InputAt(i) == nullptr) continue;
sexp->Add(InputAt(i)->ToSExpression(s));
}
}
void Instruction::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
if (GetDeoptId() != DeoptId::kNone) {
s->AddExtraInteger(sexp, "deopt_id", GetDeoptId());
}
if (env() != nullptr) {
sexp->AddExtra("env", env()->ToSExpression(s));
}
if (!token_pos().IsNoSource()) {
s->AddExtraInteger(sexp, "token_pos", token_pos().Serialize());
}
if (has_inlining_id()) {
s->AddExtraInteger(sexp, "inlining_id", inlining_id());
}
}
SExpression* Range::ToSExpression(FlowGraphSerializer* s) {
auto const sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, "Range");
sexp->Add(min_.ToSExpression(s));
if (!max_.Equals(min_)) sexp->Add(max_.ToSExpression(s));
return sexp;
}
SExpression* RangeBoundary::ToSExpression(FlowGraphSerializer* s) {
switch (kind_) {
case kSymbol: {
auto const sexp = new (s->zone()) SExpList(s->zone());
sexp->Add(s->UseToSExp(symbol()));
if (offset() != 0) {
s->AddExtraInteger(sexp, "offset", offset());
}
return sexp;
}
case kConstant:
return new (s->zone()) SExpInteger(value_);
default:
return new (s->zone()) SExpSymbol(RangeBoundary::KindToCString(kind_));
}
}
bool FlowGraphSerializer::HasDefinitionExtraInfo(const Definition* def) {
return ShouldSerializeType(def->type_) || def->range() != nullptr;
}
void FlowGraphSerializer::AddDefinitionExtraInfoToSExp(const Definition* def,
SExpList* sexp) {
// Type() isn't a const method as it can cause changes to the type_
// field, so access type_ directly instead.
if (ShouldSerializeType(def->type_)) {
sexp->AddExtra("type", def->type_->ToSExpression(this));
}
if (def->range() != nullptr) {
sexp->AddExtra("range", def->range()->ToSExpression(this));
}
}
SExpression* Definition::ToSExpression(FlowGraphSerializer* s) const {
// If we don't have a temp index, then this is a Definition that has no
// usable result.
const bool binds_name = HasSSATemp() || HasTemp();
// Don't serialize non-binding definitions as definitions unless we either
// have Definition-specific extra info or we're in verbose mode.
if (!binds_name && !FLAG_verbose_flow_graph_serialization &&
!s->HasDefinitionExtraInfo(this)) {
return Instruction::ToSExpression(s);
}
auto sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, "def");
if (binds_name) {
sexp->Add(s->UseToSExp(this));
} else {
// Since there is Definition-specific extra info to serialize, we use "_"
// as the bound name, which lets the deserializer know the result is unused.
s->AddSymbol(sexp, "_");
}
// Add only Definition-specific extra info to this form. Any extra info
// that is Instruction-specific or specific to the actual instruction type is
// added to the nested instruction form.
s->AddDefinitionExtraInfoToSExp(this, sexp);
sexp->Add(Instruction::ToSExpression(s));
return sexp;
}
void AssertAssignableInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
sexp->AddExtra("name", s->DartValueToSExp(dst_name()));
}
void ConstantInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(s->DartValueToSExp(value()));
}
void BranchInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(comparison()->ToSExpression(s));
sexp->Add(s->BlockIdToSExp(true_successor()->block_id()));
sexp->Add(s->BlockIdToSExp(false_successor()->block_id()));
}
void ParameterInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
s->AddInteger(sexp, index());
s->AddExtraInteger(sexp, "param_offset", param_offset());
s->AddExtraSymbol(sexp, "representation",
Location::RepresentationToCString(representation()));
}
void SpecialParameterInstr::AddOperandsToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
ASSERT(kind() < SpecialParameterInstr::kNumKinds);
s->AddSymbol(sexp, KindToCString(kind()));
}
SExpression* FlowGraphSerializer::LocalVariableToSExp(const LocalVariable& v) {
auto const sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "LocalVariable");
if (!v.name().IsNull()) {
AddSymbol(sexp, v.name().ToCString());
}
if (v.index().IsValid()) {
AddExtraInteger(sexp, "index", v.index().value());
}
return sexp;
}
void LoadLocalInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(s->LocalVariableToSExp(local()));
}
void StoreLocalInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(s->LocalVariableToSExp(local()));
}
SExpression* FlowGraphSerializer::SlotToSExp(const Slot& slot) {
auto sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "Slot");
AddInteger(sexp, slot.offset_in_bytes());
AddExtraSymbol(sexp, "kind", Slot::KindToCString(slot.kind()));
if (slot.IsDartField()) {
sexp->AddExtra("field", DartValueToSExp(slot.field()));
}
return sexp;
}
void LoadFieldInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(instance()->ToSExpression(s));
sexp->Add(s->SlotToSExp(slot()));
}
void LoadFieldInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (calls_initializer() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "calls_initializer", calls_initializer());
}
}
void StoreInstanceFieldInstr::AddOperandsToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(instance()->ToSExpression(s));
sexp->Add(s->SlotToSExp(slot()));
sexp->Add(value()->ToSExpression(s));
}
void StoreInstanceFieldInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (is_initialization_ || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "is_init", is_initialization_);
}
if (emit_store_barrier_ != kNoStoreBarrier ||
FLAG_verbose_flow_graph_serialization) {
// Make sure that we aren't seeing a new value added to the StoreBarrierType
// enum that isn't handled by the serializer.
ASSERT(emit_store_barrier_ == kNoStoreBarrier ||
emit_store_barrier_ == kEmitStoreBarrier);
s->AddExtraBool(sexp, "emit_barrier",
emit_store_barrier_ != kNoStoreBarrier);
}
}
void LoadIndexedUnsafeInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (offset() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "offset", offset());
}
}
void StoreIndexedUnsafeInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (offset() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "offset", offset());
}
}
void ComparisonInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
s->AddSymbol(sexp, Token::Str(kind()));
Instruction::AddOperandsToSExpression(sexp, s);
}
void StrictCompareInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (needs_number_check_ || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "needs_check", needs_number_check_);
}
}
void DoubleTestOpInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
const bool negated = kind() != Token::kEQ;
switch (op_kind()) {
case MethodRecognizer::kDouble_getIsNaN:
s->AddSymbol(sexp, negated ? "IsNotNaN" : "IsNaN");
break;
case MethodRecognizer::kDouble_getIsInfinite:
s->AddSymbol(sexp, negated ? "IsNotInfinite" : "IsInfinite");
break;
default:
UNREACHABLE();
}
sexp->Add(value()->ToSExpression(s));
}
void GotoInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
sexp->Add(s->BlockIdToSExp(successor()->block_id()));
}
void DebugStepCheckInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (stub_kind_ != UntaggedPcDescriptors::kAnyKind ||
FLAG_verbose_flow_graph_serialization) {
auto const stub_kind_name =
UntaggedPcDescriptors::KindToCString(stub_kind_);
ASSERT(stub_kind_name != nullptr);
s->AddExtraSymbol(sexp, "stub_kind", stub_kind_name);
}
}
SExpression* FlowGraphSerializer::ICDataToSExp(const ICData* ic_data) {
auto const sexp = new (zone()) SExpList(zone());
AddSymbol(sexp, "ICData");
if (ic_data->is_tracking_exactness()) {
ic_data_type_ = ic_data->receivers_static_type();
sexp->AddExtra("receivers_static_type", AbstractTypeToSExp(ic_data_type_));
}
if (ic_data->is_megamorphic() || FLAG_verbose_flow_graph_serialization) {
AddExtraBool(sexp, "is_megamorphic", ic_data->is_megamorphic());
}
auto const num_checks = ic_data->NumberOfChecks();
GrowableArray<intptr_t> class_ids(zone(), 2);
for (intptr_t i = 0; i < num_checks; i++) {
auto const entry = new (zone()) SExpList(zone());
auto const count = ic_data->GetCountAt(i);
if (count > 0 || FLAG_verbose_flow_graph_serialization) {
AddExtraInteger(entry, "count", count);
}
class_ids.Clear();
ic_data->GetCheckAt(i, &class_ids, &ic_data_target_);
entry->AddExtra("target", DartValueToSExp(ic_data_target_));
for (auto const cid : class_ids) {
entry->Add(new (zone()) SExpInteger(cid));
}
sexp->Add(entry);
}
if (FLAG_verbose_flow_graph_serialization) {
AddExtraSymbol(sexp, "rebind_rule",
ICData::RebindRuleToCString(ic_data->rebind_rule()));
tmp_string_ = ic_data->target_name();
AddExtraString(sexp, "target_name", tmp_string_.ToCString());
ic_data_target_ = ic_data->Owner();
sexp->AddExtra("owner", DartValueToSExp(ic_data_target_));
AddExtraInteger(sexp, "num_args_tested", ic_data->NumArgsTested());
auto& args_desc = Array::Handle(zone(), ic_data->arguments_descriptor());
sexp->AddExtra("arguments_descriptor", DartValueToSExp(args_desc));
}
return sexp;
}
void TailCallInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
if (auto const code = s->DartValueToSExp(code_)) {
sexp->Add(code);
}
Instruction::AddOperandsToSExpression(sexp, s);
}
void NativeCallInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddOperandsToSExpression(sexp, s);
}
void NativeCallInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s);
if (auto const func = s->DartValueToSExp(function())) {
sexp->AddExtra("function", func);
}
if (!native_name().IsNull()) {
s->AddExtraString(sexp, "name", native_name().ToCString());
}
if (link_lazily() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "link_lazily", link_lazily());
}
}
template <intptr_t kExtraInputs>
void TemplateDartCall<kExtraInputs>::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (type_args_len() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "type_args_len", type_args_len());
}
s->AddExtraInteger(sexp, "args_len", ArgumentCountWithoutTypeArgs());
const auto& arg_names = argument_names();
if (!arg_names.IsNull()) {
auto arg_names_sexp = new (s->zone()) SExpList(s->zone());
auto& str = String::Handle(s->zone());
for (intptr_t i = 0; i < arg_names.Length(); i++) {
str = String::RawCast(arg_names.At(i));
arg_names_sexp->Add(s->ObjectToSExp(str));
}
sexp->AddExtra("arg_names", arg_names_sexp);
}
ASSERT(!HasPushArguments());
}
void ClosureCallInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
// For now, just here to ensure TemplateDartCall<1>::AddExtraInfoToSExpression
// gets instantiated.
TemplateDartCall<1>::AddExtraInfoToSExpression(sexp, s);
}
void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddOperandsToSExpression(sexp, s);
}
void StaticCallInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s);
if (auto const func = s->DartValueToSExp(function())) {
sexp->AddExtra("function", func);
}
if (HasICData()) {
sexp->AddExtra("ic_data", s->ICDataToSExp(ic_data()));
} else if (CallCount() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "call_count", CallCount());
}
if (rebind_rule_ != ICData::kStatic ||
FLAG_verbose_flow_graph_serialization) {
auto const str = ICData::RebindRuleToCString(rebind_rule_);
ASSERT(str != nullptr);
s->AddExtraSymbol(sexp, "rebind_rule", str);
}
if (ShouldSerializeType(result_type())) {
sexp->AddExtra("result_type", result_type()->ToSExpression(s));
}
if (entry_kind() != Code::EntryKind::kNormal ||
FLAG_verbose_flow_graph_serialization) {
auto const kind_str = Code::EntryKindToCString(entry_kind());
s->AddExtraSymbol(sexp, "entry_kind", kind_str);
}
}
void InstanceCallBaseInstr::AddOperandsToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddOperandsToSExpression(sexp, s);
}
void InstanceCallBaseInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s);
if (auto const target = s->DartValueToSExp(interface_target())) {
sexp->AddExtra("interface_target", target);
}
if (auto const target = s->DartValueToSExp(tearoff_interface_target())) {
sexp->AddExtra("tearoff_interface_target", target);
}
if (HasICData()) {
sexp->AddExtra("ic_data", s->ICDataToSExp(ic_data()));
}
if (function_name().IsNull()) {
if (!interface_target().IsNull() || !tearoff_interface_target().IsNull()) {
s->AddExtraSymbol(sexp, "function_name", "null");
}
} else {
if (interface_target().IsNull() ||
(function_name().ptr() != interface_target().name() &&
function_name().ptr() != tearoff_interface_target().name())) {
s->AddExtraString(sexp, "function_name", function_name().ToCString());
}
}
if (token_kind() != Token::kILLEGAL) {
s->AddExtraSymbol(sexp, "token_kind", Token::Str(token_kind()));
}
if (ShouldSerializeType(result_type())) {
sexp->AddExtra("result_type", result_type()->ToSExpression(s));
}
if (entry_kind() != Code::EntryKind::kNormal ||
FLAG_verbose_flow_graph_serialization) {
auto const kind_str = Code::EntryKindToCString(entry_kind());
s->AddExtraSymbol(sexp, "entry_kind", kind_str);
}
}
void InstanceCallInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
InstanceCallBaseInstr::AddExtraInfoToSExpression(sexp, s);
if (checked_argument_count() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "checked_arg_count", checked_argument_count());
}
}
void PolymorphicInstanceCallInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
InstanceCallBaseInstr::AddExtraInfoToSExpression(sexp, s);
if (targets().length() > 0 || FLAG_verbose_flow_graph_serialization) {
auto elem_list = new (s->zone()) SExpList(s->zone());
for (intptr_t i = 0; i < targets().length(); i++) {
auto elem = new (s->zone()) SExpList(s->zone());
const TargetInfo* ti = targets().TargetAt(i);
if (ti->cid_start == ti->cid_end) {
s->AddInteger(elem, ti->cid_start);
} else {
auto range = new (s->zone()) SExpList(s->zone());
s->AddInteger(range, ti->cid_start);
s->AddInteger(range, ti->cid_end);
elem->Add(range);
}
if (auto const target = s->DartValueToSExp(*ti->target)) {
elem->Add(target);
}
elem_list->Add(elem);
}
sexp->AddExtra("targets", elem_list);
}
}
void AllocateObjectInstr::AddOperandsToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
if (auto const sexp_cls = s->DartValueToSExp(cls())) {
sexp->Add(sexp_cls);
}
if (type_arguments() != nullptr) {
sexp->Add(type_arguments()->ToSExpression(s));
}
}
void AllocateObjectInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
s->AddExtraInteger(sexp, "size", cls().target_instance_size());
if (auto const closure = s->DartValueToSExp(closure_function())) {
sexp->AddExtra("closure_function", closure);
}
if (!Identity().IsUnknown() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraSymbol(sexp, "identity", Identity().ToCString());
}
}
void BinaryIntegerOpInstr::AddOperandsToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
s->AddSymbol(sexp, Token::Str(op_kind()));
sexp->Add(left()->ToSExpression(s));
sexp->Add(right()->ToSExpression(s));
}
void CheckedSmiOpInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
s->AddSymbol(sexp, Token::Str(op_kind()));
sexp->Add(left()->ToSExpression(s));
sexp->Add(right()->ToSExpression(s));
}
// clang-format off
static const char* simd_op_kind_string[] = {
#define CASE(Arity, Mask, Name, ...) #Name,
SIMD_OP_LIST(CASE, CASE)
#undef CASE
"IllegalSimdOp",
};
// clang-format on
void SimdOpInstr::AddOperandsToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
s->AddSymbol(sexp, simd_op_kind_string[kind()]);
Instruction::AddOperandsToSExpression(sexp, s);
}
void SimdOpInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (HasMask()) {
s->AddExtraInteger(sexp, "mask", mask());
}
}
void LoadIndexedInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (aligned() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "aligned", aligned());
}
if (index_scale() > 1 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "scale", index_scale());
}
if (class_id() != kDynamicCid || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "cid", class_id());
}
}
void StoreIndexedInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (aligned() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "aligned", aligned());
}
if (index_scale() > 1 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "scale", index_scale());
}
if (class_id() != kDynamicCid || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "cid", class_id());
}
}
void CheckStackOverflowInstr::AddExtraInfoToSExpression(
SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (stack_depth() > 0 || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "stack_depth", stack_depth());
}
if (in_loop() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "loop_depth", loop_depth());
}
if (kind_ != kOsrAndPreemption) {
ASSERT(kind_ == kOsrOnly);
s->AddExtraSymbol(sexp, "kind", "OsrOnly");
}
}
void CheckNullInstr::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
Instruction::AddExtraInfoToSExpression(sexp, s);
if (!function_name_.IsNull()) {
s->AddExtraString(sexp, "function_name", function_name_.ToCString());
}
}
SExpression* Value::ToSExpression(FlowGraphSerializer* s) const {
auto name = s->UseToSExp(definition());
// If we're not serializing types or there is no reaching type for this use,
// just serialize the use as the bound name.
if (!ShouldSerializeType(reaching_type_)) return name;
auto sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, "value");
sexp->Add(name);
// If there is no owner for the type, then serialize the type in full.
// Otherwise the owner should be the definition, so we'll inherit the type
// from it. (That is, (value v<X>) with no explicit type info means the
// reaching type comes from the definition of v<X>.) We'll serialize an
// "inherit_type" extra info field to make this explicit when in verbose mode.
if (reaching_type_->owner() == nullptr) {
sexp->AddExtra("type", reaching_type_->ToSExpression(s));
} else {
ASSERT(reaching_type_->owner() == definition());
}
if (FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "inherit_type",
reaching_type_->owner() == definition());
}
return sexp;
}
SExpression* CompileType::ToSExpression(FlowGraphSerializer* s) const {
ASSERT(FLAG_verbose_flow_graph_serialization ||
FLAG_serialize_flow_graph_types);
auto sexp = new (s->zone()) SExpList(s->zone());
s->AddSymbol(sexp, "CompileType");
AddExtraInfoToSExpression(sexp, s);
return sexp;
}
void CompileType::AddExtraInfoToSExpression(SExpList* sexp,
FlowGraphSerializer* s) const {
if (cid_ != kIllegalCid || FLAG_verbose_flow_graph_serialization) {
s->AddExtraInteger(sexp, "cid", cid_);
}
// TODO(sstrickl): Currently we only print out nullable if it's false
// (or during verbose printing). Switch this when NNBD is the standard.
if (!is_nullable() || FLAG_verbose_flow_graph_serialization) {
s->AddExtraBool(sexp, "nullable", is_nullable());
}
if (type_ != nullptr) {
sexp->AddExtra("type", s->DartValueToSExp(*type_));
}
}
SExpression* Environment::ToSExpression(FlowGraphSerializer* s) const {
auto sexp = new (s->zone()) SExpList(s->zone());
for (intptr_t i = 0; i < values_.length(); ++i) {
ASSERT(!values_[i]->definition()->IsPushArgument());
sexp->Add(values_[i]->ToSExpression(s));
// TODO(sstrickl): This currently assumes that there are no locations in the
// environment (e.g. before register allocation). If we ever want to print
// out environments on steps after AllocateRegisters, we'll need to handle
// locations as well.
ASSERT(locations_ == nullptr || locations_[i].IsInvalid());
}
if (outer_ != NULL) {
auto outer_sexp = outer_->ToSExpression(s)->AsList();
if (outer_->deopt_id_ != DeoptId::kNone) {
s->AddExtraInteger(outer_sexp, "deopt_id", outer_->deopt_id_);
}
sexp->AddExtra("outer", outer_sexp);
}
return sexp;
}
} // namespace dart