| // 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()->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_function_(Function::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"); |
| tmp_string_ = param.name(); |
| AddSymbol(sexp, tmp_string_.ToCString()); |
| if (param.IsFunctionTypeParameter()) { |
| if (param.parameterized_function() != flow_graph_->function().raw()) { |
| type_function_ = param.parameterized_function(); |
| sexp->AddExtra("function", CanonicalNameToSExp(type_function_)); |
| } else if (FLAG_verbose_flow_graph_serialization) { |
| sexp->AddExtra("function", |
| CanonicalNameToSExp(flow_graph_->function())); |
| } |
| } else if (param.IsClassTypeParameter()) { |
| type_class_ = param.parameterized_class(); |
| AddExtraInteger(sexp, "class", type_class_.id()); |
| } |
| 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; |
| } |
| ASSERT(t.IsType()); |
| AddSymbol(sexp, "Type"); |
| const auto& type = Type::Cast(t); |
| if (!type.token_pos().IsNoSource()) { |
| AddExtraInteger(sexp, "token_pos", type.token_pos().value()); |
| } |
| // 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 = type.IsRecursive(); |
| intptr_t hash = 0; |
| if (is_recursive) { |
| hash = type.Hash(); |
| AddExtraInteger(sexp, "hash", hash); |
| open_recursive_types_.Insert(hash, &type); |
| } |
| 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()); |
| } |
| if (type.IsFunctionType()) { |
| type_function_ = type.signature(); |
| sexp->AddExtra("signature", DartValueToSExp(type_function_)); |
| } |
| // 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() != FunctionLayout::Kind::kRegularFunction || |
| FLAG_verbose_flow_graph_serialization) { |
| AddExtraSymbol(sexp, "kind", FunctionLayout::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::ArrayToSExp(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::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.raw() == Object::sentinel().raw()) { |
| 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 ArrayToSExp(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().value()); |
| } |
| } |
| |
| 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_ != PcDescriptorsLayout::kAnyKind || |
| FLAG_verbose_flow_graph_serialization) { |
| auto const stub_kind_name = PcDescriptorsLayout::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().raw() != interface_target().name() && |
| function_name().raw() != 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 |