| 1 | // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/compiler/backend/il_serializer.h" |
| 6 | |
| 7 | #include "vm/compiler/backend/flow_graph.h" |
| 8 | #include "vm/compiler/backend/il.h" |
| 9 | #include "vm/compiler/backend/range_analysis.h" |
| 10 | #include "vm/compiler/method_recognizer.h" |
| 11 | #include "vm/object_store.h" |
| 12 | #include "vm/os.h" |
| 13 | #include "vm/zone_text_buffer.h" |
| 14 | |
| 15 | namespace dart { |
| 16 | |
| 17 | DEFINE_FLAG(bool, |
| 18 | serialize_flow_graph_types, |
| 19 | true, |
| 20 | "Serialize inferred type information in flow graphs" ); |
| 21 | |
| 22 | DEFINE_FLAG(bool, |
| 23 | verbose_flow_graph_serialization, |
| 24 | false, |
| 25 | "Serialize extra information useful for debugging" ); |
| 26 | |
| 27 | DEFINE_FLAG(bool, |
| 28 | pretty_print_serialization, |
| 29 | false, |
| 30 | "Format serialized output nicely" ); |
| 31 | |
| 32 | DECLARE_FLAG(bool, populate_llvm_constant_pool); |
| 33 | |
| 34 | const char* const FlowGraphSerializer::initial_indent = "" ; |
| 35 | |
| 36 | FlowGraphSerializer::FlowGraphSerializer(Zone* zone, |
| 37 | const FlowGraph* flow_graph) |
| 38 | : flow_graph_(ASSERT_NOTNULL(flow_graph)), |
| 39 | zone_(zone), |
| 40 | object_store_(flow_graph->thread()->isolate()->object_store()), |
| 41 | open_recursive_types_(zone_), |
| 42 | llvm_constants_( |
| 43 | GrowableObjectArray::Handle(zone_, |
| 44 | object_store_->llvm_constant_pool())), |
| 45 | llvm_functions_( |
| 46 | GrowableObjectArray::Handle(zone_, |
| 47 | object_store_->llvm_function_pool())), |
| 48 | llvm_constant_map_(zone_, object_store_->llvm_constant_hash_table()), |
| 49 | llvm_index_(Smi::Handle(zone_)), |
| 50 | tmp_string_(String::Handle(zone_)), |
| 51 | array_type_args_((TypeArguments::Handle(zone_))), |
| 52 | closure_context_(Context::Handle(zone_)), |
| 53 | closure_function_(Function::Handle(zone_)), |
| 54 | closure_type_args_(TypeArguments::Handle(zone_)), |
| 55 | code_owner_(Object::Handle(zone_)), |
| 56 | context_parent_(Context::Handle(zone_)), |
| 57 | context_elem_(Object::Handle(zone_)), |
| 58 | function_type_args_(TypeArguments::Handle(zone_)), |
| 59 | ic_data_target_(Function::Handle(zone_)), |
| 60 | ic_data_type_(AbstractType::Handle(zone_)), |
| 61 | instance_field_(Field::Handle(zone_)), |
| 62 | instance_type_args_(TypeArguments::Handle(zone_)), |
| 63 | serialize_library_(Library::Handle(zone_)), |
| 64 | serialize_owner_(Class::Handle(zone_)), |
| 65 | serialize_parent_(Function::Handle(zone_)), |
| 66 | type_arguments_elem_(AbstractType::Handle(zone_)), |
| 67 | type_class_(Class::Handle(zone_)), |
| 68 | type_function_(Function::Handle(zone_)), |
| 69 | type_ref_type_(AbstractType::Handle(zone_)) { |
| 70 | // Double-check that the zone in the flow graph is a parent of the |
| 71 | // zone we'll be using for serialization. |
| 72 | ASSERT(flow_graph->zone()->ContainsNestedZone(zone)); |
| 73 | } |
| 74 | |
| 75 | FlowGraphSerializer::~FlowGraphSerializer() { |
| 76 | object_store_->set_llvm_constant_hash_table(llvm_constant_map_.Release()); |
| 77 | } |
| 78 | |
| 79 | void FlowGraphSerializer::SerializeToBuffer(Zone* zone, |
| 80 | const FlowGraph* flow_graph, |
| 81 | BaseTextBuffer* buffer) { |
| 82 | ASSERT(buffer != nullptr); |
| 83 | auto const sexp = SerializeToSExp(zone, flow_graph); |
| 84 | if (FLAG_pretty_print_serialization) { |
| 85 | sexp->SerializeTo(zone, buffer, initial_indent); |
| 86 | } else { |
| 87 | sexp->SerializeToLine(buffer); |
| 88 | } |
| 89 | buffer->AddString("\n\n" ); |
| 90 | } |
| 91 | |
| 92 | SExpression* FlowGraphSerializer::SerializeToSExp(Zone* zone, |
| 93 | const FlowGraph* flow_graph) { |
| 94 | FlowGraphSerializer serializer(zone, flow_graph); |
| 95 | return serializer.FlowGraphToSExp(); |
| 96 | } |
| 97 | |
| 98 | #define KIND_STR(name) #name, |
| 99 | static const char* block_entry_kind_tags[FlowGraphSerializer::kNumEntryKinds] = |
| 100 | {FOR_EACH_BLOCK_ENTRY_KIND(KIND_STR)}; |
| 101 | #undef KIND_STR |
| 102 | |
| 103 | FlowGraphSerializer::BlockEntryKind FlowGraphSerializer::BlockEntryTagToKind( |
| 104 | SExpSymbol* tag) { |
| 105 | if (tag == nullptr) return kTarget; |
| 106 | auto const str = tag->value(); |
| 107 | for (intptr_t i = 0; i < kNumEntryKinds; i++) { |
| 108 | auto const current = block_entry_kind_tags[i]; |
| 109 | if (strcmp(str, current) == 0) return static_cast<BlockEntryKind>(i); |
| 110 | } |
| 111 | return kInvalid; |
| 112 | } |
| 113 | |
| 114 | void FlowGraphSerializer::AddBool(SExpList* sexp, bool b) { |
| 115 | sexp->Add(new (zone()) SExpBool(b)); |
| 116 | } |
| 117 | |
| 118 | void FlowGraphSerializer::AddInteger(SExpList* sexp, intptr_t i) { |
| 119 | sexp->Add(new (zone()) SExpInteger(i)); |
| 120 | } |
| 121 | |
| 122 | void FlowGraphSerializer::AddString(SExpList* sexp, const char* cstr) { |
| 123 | sexp->Add(new (zone()) SExpString(cstr)); |
| 124 | } |
| 125 | |
| 126 | void FlowGraphSerializer::AddSymbol(SExpList* sexp, const char* cstr) { |
| 127 | sexp->Add(new (zone()) SExpSymbol(cstr)); |
| 128 | } |
| 129 | |
| 130 | void FlowGraphSerializer::(SExpList* sexp, |
| 131 | const char* label, |
| 132 | bool b) { |
| 133 | sexp->AddExtra(label, new (zone()) SExpBool(b)); |
| 134 | } |
| 135 | |
| 136 | void FlowGraphSerializer::(SExpList* sexp, |
| 137 | const char* label, |
| 138 | intptr_t i) { |
| 139 | sexp->AddExtra(label, new (zone()) SExpInteger(i)); |
| 140 | } |
| 141 | |
| 142 | void FlowGraphSerializer::(SExpList* sexp, |
| 143 | const char* label, |
| 144 | const char* cstr) { |
| 145 | sexp->AddExtra(label, new (zone()) SExpString(cstr)); |
| 146 | } |
| 147 | |
| 148 | void FlowGraphSerializer::(SExpList* sexp, |
| 149 | const char* label, |
| 150 | const char* cstr) { |
| 151 | sexp->AddExtra(label, new (zone()) SExpSymbol(cstr)); |
| 152 | } |
| 153 | |
| 154 | SExpression* FlowGraphSerializer::BlockIdToSExp(intptr_t block_id) { |
| 155 | return new (zone()) SExpSymbol(OS::SCreate(zone(), "B%" Pd "" , block_id)); |
| 156 | } |
| 157 | |
| 158 | void FlowGraphSerializer::SerializeCanonicalName(BaseTextBuffer* b, |
| 159 | const Object& obj) { |
| 160 | ASSERT(!obj.IsNull()); |
| 161 | if (obj.IsFunction()) { |
| 162 | const auto& function = Function::Cast(obj); |
| 163 | tmp_string_ = function.name(); |
| 164 | // We only want private keys removed, no other changes. |
| 165 | tmp_string_ = String::RemovePrivateKey(tmp_string_); |
| 166 | const char* function_name = tmp_string_.ToCString(); |
| 167 | // If this function is an inner closure then the parent points to its |
| 168 | // containing function, which will also be part of the canonical name. |
| 169 | // |
| 170 | // We retrieve the owner before retrieving the parent function, as the |
| 171 | // inner closure chain may be arbitrarily deep and serialize_parent_ is |
| 172 | // passed in on recursive calls. When it is, then changing serialize_parent_ |
| 173 | // to the parent function also changes the contents of obj and thus we'd |
| 174 | // no longer be able to retrieve the child function or its owner. |
| 175 | // |
| 176 | // This does mean that serialize_owner_ gets overwritten for each recursive |
| 177 | // call until we reach the end of the chain, but we only use its contents at |
| 178 | // the end of the chain anyway. |
| 179 | serialize_owner_ = function.Owner(); |
| 180 | serialize_parent_ = function.parent_function(); |
| 181 | if (!serialize_parent_.IsNull()) { |
| 182 | SerializeCanonicalName(b, serialize_parent_); |
| 183 | } else { |
| 184 | ASSERT(!serialize_owner_.IsNull()); |
| 185 | SerializeCanonicalName(b, serialize_owner_); |
| 186 | } |
| 187 | b->Printf(":%s" , function_name); |
| 188 | } else if (obj.IsClass()) { |
| 189 | const auto& cls = Class::Cast(obj); |
| 190 | tmp_string_ = cls.ScrubbedName(); |
| 191 | const char* class_name = tmp_string_.ToCString(); |
| 192 | serialize_library_ = cls.library(); |
| 193 | if (!serialize_library_.IsNull()) { |
| 194 | SerializeCanonicalName(b, serialize_library_); |
| 195 | } |
| 196 | b->Printf(":%s" , class_name); |
| 197 | } else if (obj.IsLibrary()) { |
| 198 | const Library& lib = Library::Cast(obj); |
| 199 | tmp_string_ = lib.url(); |
| 200 | const char* lib_name = tmp_string_.ToCString(); |
| 201 | if (lib_name[0] == '\0') return; |
| 202 | b->AddString(lib_name); |
| 203 | } else if (obj.IsField()) { |
| 204 | const auto& field = Field::Cast(obj); |
| 205 | tmp_string_ = field.UserVisibleName(); |
| 206 | const char* field_name = tmp_string_.ToCString(); |
| 207 | serialize_owner_ = field.Owner(); |
| 208 | ASSERT(!serialize_owner_.IsNull()); |
| 209 | SerializeCanonicalName(b, serialize_owner_); |
| 210 | b->Printf(".%s" , field_name); |
| 211 | } else { |
| 212 | UNREACHABLE(); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | SExpression* FlowGraphSerializer::CanonicalNameToSExp(const Object& obj) { |
| 217 | ASSERT(!obj.IsNull()); |
| 218 | ZoneTextBuffer b(zone_, 100); |
| 219 | SerializeCanonicalName(&b, obj); |
| 220 | return new (zone()) SExpSymbol(b.buffer()); |
| 221 | } |
| 222 | |
| 223 | SExpSymbol* FlowGraphSerializer::BlockEntryKindToTag(BlockEntryKind k) { |
| 224 | ASSERT(k >= 0 && k < kNumEntryKinds); |
| 225 | return new (zone()) SExpSymbol(block_entry_kind_tags[k]); |
| 226 | } |
| 227 | |
| 228 | #define KIND_TAG(name) block_entry_kind_tags[k##name] |
| 229 | SExpSymbol* FlowGraphSerializer::BlockEntryTag(const BlockEntryInstr* entry) { |
| 230 | if (entry == nullptr) return nullptr; |
| 231 | if (entry->IsGraphEntry()) { |
| 232 | return BlockEntryKindToTag(kGraph); |
| 233 | } |
| 234 | if (entry->IsOsrEntry()) { |
| 235 | return BlockEntryKindToTag(kOSR); |
| 236 | } |
| 237 | if (entry->IsCatchBlockEntry()) { |
| 238 | return BlockEntryKindToTag(kCatch); |
| 239 | } |
| 240 | if (entry->IsIndirectEntry()) { |
| 241 | return BlockEntryKindToTag(kIndirect); |
| 242 | } |
| 243 | if (entry->IsFunctionEntry()) { |
| 244 | if (entry == flow_graph()->graph_entry()->normal_entry()) { |
| 245 | return BlockEntryKindToTag(kNormal); |
| 246 | } |
| 247 | if (entry == flow_graph()->graph_entry()->unchecked_entry()) { |
| 248 | return BlockEntryKindToTag(kUnchecked); |
| 249 | } |
| 250 | } |
| 251 | if (entry->IsJoinEntry()) { |
| 252 | return BlockEntryKindToTag(kJoin); |
| 253 | } |
| 254 | return nullptr; |
| 255 | } |
| 256 | #undef KIND_TAG |
| 257 | |
| 258 | SExpression* FlowGraphSerializer::FunctionEntryToSExp( |
| 259 | const BlockEntryInstr* entry) { |
| 260 | if (entry == nullptr) return nullptr; |
| 261 | auto sexp = new (zone()) SExpList(zone()); |
| 262 | sexp->Add(BlockEntryTag(entry)); |
| 263 | sexp->Add(BlockIdToSExp(entry->block_id())); |
| 264 | if (auto const with_defs = entry->AsBlockEntryWithInitialDefs()) { |
| 265 | auto const initial_defs = with_defs->initial_definitions(); |
| 266 | for (intptr_t i = 0; i < initial_defs->length(); i++) { |
| 267 | sexp->Add(initial_defs->At(i)->ToSExpression(this)); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | // Also include the extra info here, to avoid having to find the |
| 272 | // corresponding block to get it. |
| 273 | entry->BlockEntryInstr::AddExtraInfoToSExpression(sexp, this); |
| 274 | |
| 275 | return sexp; |
| 276 | } |
| 277 | |
| 278 | SExpression* FlowGraphSerializer::EntriesToSExp(const GraphEntryInstr* start) { |
| 279 | auto sexp = new (zone()) SExpList(zone()); |
| 280 | AddSymbol(sexp, "Entries" ); |
| 281 | if (auto const normal = FunctionEntryToSExp(start->normal_entry())) { |
| 282 | sexp->Add(normal); |
| 283 | } |
| 284 | if (auto const unchecked = FunctionEntryToSExp(start->unchecked_entry())) { |
| 285 | sexp->Add(unchecked); |
| 286 | } |
| 287 | if (auto const osr = FunctionEntryToSExp(start->osr_entry())) { |
| 288 | sexp->Add(osr); |
| 289 | } |
| 290 | for (intptr_t i = 0; i < start->catch_entries().length(); i++) { |
| 291 | sexp->Add(FunctionEntryToSExp(start->catch_entries().At(i))); |
| 292 | } |
| 293 | for (intptr_t i = 0; i < start->indirect_entries().length(); i++) { |
| 294 | sexp->Add(FunctionEntryToSExp(start->indirect_entries().At(i))); |
| 295 | } |
| 296 | return sexp; |
| 297 | } |
| 298 | |
| 299 | SExpression* FlowGraphSerializer::FlowGraphToSExp() { |
| 300 | auto const start = flow_graph()->graph_entry(); |
| 301 | auto const sexp = new (zone()) SExpList(zone()); |
| 302 | AddSymbol(sexp, "FlowGraph" ); |
| 303 | sexp->Add(CanonicalNameToSExp(flow_graph()->function())); |
| 304 | AddExtraInteger(sexp, "deopt_id" , start->deopt_id()); |
| 305 | if (start->env() != nullptr) { |
| 306 | sexp->AddExtra("env" , start->env()->ToSExpression(this)); |
| 307 | } |
| 308 | if (start->IsCompiledForOsr()) { |
| 309 | AddExtraInteger(sexp, "osr_id" , start->osr_id()); |
| 310 | } |
| 311 | if (auto const constants = ConstantPoolToSExp(start)) { |
| 312 | sexp->Add(constants); |
| 313 | } |
| 314 | sexp->Add(EntriesToSExp(start)); |
| 315 | auto& block_order = flow_graph()->reverse_postorder(); |
| 316 | // Skip the first block, which will be the graph entry block (B0). We |
| 317 | // output all its information as part of the function expression, so it'll |
| 318 | // just show up as an empty block here. |
| 319 | ASSERT(block_order[0]->IsGraphEntry()); |
| 320 | for (intptr_t i = 1; i < block_order.length(); ++i) { |
| 321 | sexp->Add(block_order[i]->ToSExpression(this)); |
| 322 | } |
| 323 | if (FLAG_populate_llvm_constant_pool) { |
| 324 | auto const new_index = llvm_functions_.Length(); |
| 325 | llvm_functions_.Add(flow_graph_->function()); |
| 326 | AddExtraInteger(sexp, "llvm_index" , new_index); |
| 327 | } |
| 328 | return sexp; |
| 329 | } |
| 330 | |
| 331 | SExpression* FlowGraphSerializer::UseToSExp(const Definition* definition) { |
| 332 | ASSERT(definition != nullptr); |
| 333 | ASSERT(definition->HasSSATemp() || definition->HasTemp()); |
| 334 | if (definition->HasSSATemp()) { |
| 335 | const intptr_t temp_index = definition->ssa_temp_index(); |
| 336 | const auto name_cstr = OS::SCreate(zone(), "v%" Pd "" , temp_index); |
| 337 | if (definition->HasPairRepresentation()) { |
| 338 | auto sexp = new (zone()) SExpList(zone()); |
| 339 | AddSymbol(sexp, name_cstr); |
| 340 | AddSymbol(sexp, OS::SCreate(zone(), "v%" Pd "" , temp_index + 1)); |
| 341 | return sexp; |
| 342 | } else { |
| 343 | return new (zone()) SExpSymbol(name_cstr); |
| 344 | } |
| 345 | } else if (definition->HasTemp()) { |
| 346 | const intptr_t temp_index = definition->temp_index(); |
| 347 | return new (zone()) SExpSymbol(OS::SCreate(zone(), "t%" Pd "" , temp_index)); |
| 348 | } |
| 349 | UNREACHABLE(); |
| 350 | } |
| 351 | |
| 352 | SExpression* FlowGraphSerializer::ClassToSExp(const Class& cls) { |
| 353 | if (cls.IsNull()) return nullptr; |
| 354 | auto sexp = new (zone()) SExpList(zone()); |
| 355 | AddSymbol(sexp, "Class" ); |
| 356 | AddInteger(sexp, cls.id()); |
| 357 | if (FLAG_verbose_flow_graph_serialization) { |
| 358 | sexp->AddExtra("name" , CanonicalNameToSExp(cls)); |
| 359 | // Currently, AbstractTypeToSExp assumes that serializing a class cannot |
| 360 | // re-enter it. If we make that possible by serializing parts of a class |
| 361 | // that can contain AbstractTypes, especially types that are not type |
| 362 | // parameters or type references, fix AbstractTypeToSExp appropriately. |
| 363 | } |
| 364 | return sexp; |
| 365 | } |
| 366 | |
| 367 | static bool ShouldSerializeType(CompileType* type) { |
| 368 | return (FLAG_verbose_flow_graph_serialization || |
| 369 | FLAG_serialize_flow_graph_types) && |
| 370 | type != nullptr; |
| 371 | } |
| 372 | |
| 373 | SExpression* FlowGraphSerializer::FieldToSExp(const Field& field) { |
| 374 | if (field.IsNull()) return nullptr; |
| 375 | auto sexp = new (zone()) SExpList(zone()); |
| 376 | AddSymbol(sexp, "Field" ); |
| 377 | sexp->Add(CanonicalNameToSExp(field)); |
| 378 | CompileType t(field.is_nullable(), field.guarded_cid(), nullptr); |
| 379 | if (ShouldSerializeType(&t)) { |
| 380 | sexp->AddExtra("type" , t.ToSExpression(this)); |
| 381 | } |
| 382 | return sexp; |
| 383 | } |
| 384 | |
| 385 | SExpression* FlowGraphSerializer::AbstractTypeToSExp(const AbstractType& t) { |
| 386 | if (t.IsNull()) return nullptr; |
| 387 | ASSERT(t.IsFinalized()); |
| 388 | auto sexp = new (zone()) SExpList(zone()); |
| 389 | if (t.IsTypeParameter()) { |
| 390 | const auto& param = TypeParameter::Cast(t); |
| 391 | AddSymbol(sexp, "TypeParameter" ); |
| 392 | tmp_string_ = param.name(); |
| 393 | AddSymbol(sexp, tmp_string_.ToCString()); |
| 394 | if (param.IsFunctionTypeParameter()) { |
| 395 | if (param.parameterized_function() != flow_graph_->function().raw()) { |
| 396 | type_function_ = param.parameterized_function(); |
| 397 | sexp->AddExtra("function" , CanonicalNameToSExp(type_function_)); |
| 398 | } else if (FLAG_verbose_flow_graph_serialization) { |
| 399 | sexp->AddExtra("function" , |
| 400 | CanonicalNameToSExp(flow_graph_->function())); |
| 401 | } |
| 402 | } else if (param.IsClassTypeParameter()) { |
| 403 | type_class_ = param.parameterized_class(); |
| 404 | AddExtraInteger(sexp, "class" , type_class_.id()); |
| 405 | } |
| 406 | return sexp; |
| 407 | } |
| 408 | if (t.IsTypeRef()) { |
| 409 | const auto& ref = TypeRef::Cast(t); |
| 410 | AddSymbol(sexp, "TypeRef" ); |
| 411 | type_ref_type_ = ref.type(); |
| 412 | auto const hash = type_ref_type_.Hash(); |
| 413 | // Check to see if this is a TypeRef to a type we're currently serializing. |
| 414 | // If it is not, then we need to serialize the underlying type, as it |
| 415 | // otherwise won't be available when deserializing. |
| 416 | auto const open_type = open_recursive_types_.LookupValue(hash); |
| 417 | if (open_type == nullptr) { |
| 418 | // Allocate a new handle as we may re-enter the TypeRef branch. |
| 419 | auto& type = AbstractType::Handle(zone(), ref.type()); |
| 420 | sexp->Add(AbstractTypeToSExp(type)); |
| 421 | // If we serialized the referrent, then we don't need this information, |
| 422 | // but it may be useful for debugging so add it in verbose mode. |
| 423 | if (FLAG_verbose_flow_graph_serialization) { |
| 424 | AddExtraInteger(sexp, "hash" , hash); |
| 425 | } |
| 426 | } else { |
| 427 | // Make sure we didn't have a hash collision. |
| 428 | ASSERT(open_type->Equals(type_ref_type_)); |
| 429 | AddExtraInteger(sexp, "hash" , hash); |
| 430 | } |
| 431 | if (FLAG_verbose_flow_graph_serialization) { |
| 432 | AddExtraString(sexp, "type" , type_ref_type_.ToCString()); |
| 433 | } |
| 434 | return sexp; |
| 435 | } |
| 436 | ASSERT(t.IsType()); |
| 437 | AddSymbol(sexp, "Type" ); |
| 438 | const auto& type = Type::Cast(t); |
| 439 | if (!type.token_pos().IsNoSource()) { |
| 440 | AddExtraInteger(sexp, "token_pos" , type.token_pos().value()); |
| 441 | } |
| 442 | // We want to check for the type being recursive before we may serialize |
| 443 | // any sub-parts that include possible TypeRefs to this type. |
| 444 | const bool is_recursive = type.IsRecursive(); |
| 445 | intptr_t hash = 0; |
| 446 | if (is_recursive) { |
| 447 | hash = type.Hash(); |
| 448 | AddExtraInteger(sexp, "hash" , hash); |
| 449 | open_recursive_types_.Insert(hash, &type); |
| 450 | } |
| 451 | if (type.HasTypeClass()) { |
| 452 | type_class_ = type.type_class(); |
| 453 | // This avoids re-entry as long as serializing a class doesn't involve |
| 454 | // serializing concrete (non-parameter, non-reference) types. |
| 455 | sexp->Add(DartValueToSExp(type_class_)); |
| 456 | } else { |
| 457 | // TODO(dartbug.com/36882): Actually structure non-class types instead of |
| 458 | // just printing out this version. |
| 459 | AddExtraString(sexp, "name" , type.ToCString()); |
| 460 | } |
| 461 | if (type.IsFunctionType()) { |
| 462 | type_function_ = type.signature(); |
| 463 | sexp->AddExtra("signature" , DartValueToSExp(type_function_)); |
| 464 | } |
| 465 | // Since type arguments may themselves be instantiations of generic |
| 466 | // types, we may call back into this function in the middle of printing |
| 467 | // the TypeArguments and so we must allocate a fresh handle here. |
| 468 | const auto& args = TypeArguments::Handle(zone(), type.arguments()); |
| 469 | if (auto const args_sexp = NonEmptyTypeArgumentsToSExp(args)) { |
| 470 | sexp->AddExtra("type_args" , args_sexp); |
| 471 | } |
| 472 | // If we were parsing a recursive type, we're now done building it, so |
| 473 | // remove it from the open recursive types. |
| 474 | if (is_recursive) open_recursive_types_.Remove(hash); |
| 475 | return sexp; |
| 476 | } |
| 477 | |
| 478 | SExpression* FlowGraphSerializer::CodeToSExp(const Code& code) { |
| 479 | if (code.IsNull()) return nullptr; |
| 480 | auto sexp = new (zone()) SExpList(zone()); |
| 481 | AddSymbol(sexp, "Code" ); |
| 482 | if (code.IsStubCode()) { |
| 483 | AddSymbol(sexp, StubCode::NameOfStub(code.EntryPoint())); |
| 484 | if (FLAG_verbose_flow_graph_serialization) { |
| 485 | AddExtraSymbol(sexp, "kind" , "stub" ); |
| 486 | } |
| 487 | return sexp; |
| 488 | } |
| 489 | code_owner_ = code.owner(); |
| 490 | if (!code_owner_.IsNull() && FLAG_verbose_flow_graph_serialization) { |
| 491 | if (code_owner_.IsClass()) { |
| 492 | AddExtraSymbol(sexp, "kind" , "allocate" ); |
| 493 | } else if (code_owner_.IsAbstractType()) { |
| 494 | AddExtraSymbol(sexp, "kind" , "type_test" ); |
| 495 | } else { |
| 496 | ASSERT(code_owner_.IsFunction()); |
| 497 | AddExtraSymbol(sexp, "kind" , "function" ); |
| 498 | } |
| 499 | } |
| 500 | sexp->Add(DartValueToSExp(code_owner_)); |
| 501 | return sexp; |
| 502 | } |
| 503 | |
| 504 | SExpression* FlowGraphSerializer::TypeArgumentsToSExp(const TypeArguments& ta) { |
| 505 | if (ta.IsNull()) return nullptr; |
| 506 | auto sexp = new (zone()) SExpList(zone()); |
| 507 | AddSymbol(sexp, "TypeArguments" ); |
| 508 | for (intptr_t i = 0; i < ta.Length(); i++) { |
| 509 | type_arguments_elem_ = ta.TypeAt(i); |
| 510 | sexp->Add(DartValueToSExp(type_arguments_elem_)); |
| 511 | } |
| 512 | if (FLAG_verbose_flow_graph_serialization && ta.IsRecursive()) { |
| 513 | AddExtraInteger(sexp, "hash" , ta.Hash()); |
| 514 | } |
| 515 | return sexp; |
| 516 | } |
| 517 | |
| 518 | SExpression* FlowGraphSerializer::InstanceToSExp(const Instance& inst) { |
| 519 | if (inst.IsNull()) return nullptr; |
| 520 | |
| 521 | // Since InstanceToSExp may use ObjectToSExp (via DartValueToSExp) for field |
| 522 | // values that aren't entries in the constant pool, and ObjectToSExp may |
| 523 | // re-enter InstanceToSExp, allocate fresh handles here for the argument to |
| 524 | // DartValueToSExp and other handles that are live across the call. |
| 525 | const auto& instance_class = Class::Handle(zone(), inst.clazz()); |
| 526 | const auto& instance_fields_array = |
| 527 | Array::Handle(zone(), instance_class.fields()); |
| 528 | auto& instance_field_value = Object::Handle(zone()); |
| 529 | |
| 530 | auto const sexp = new (zone()) SExpList(zone()); |
| 531 | AddSymbol(sexp, "Instance" ); |
| 532 | AddInteger(sexp, instance_class.id()); |
| 533 | auto const fields = new (zone()) SExpList(zone()); |
| 534 | AddSymbol(fields, "Fields" ); |
| 535 | for (intptr_t i = 0; i < instance_fields_array.Length(); i++) { |
| 536 | instance_field_ = Field::RawCast(instance_fields_array.At(i)); |
| 537 | // We don't need to serialize static fields, since they're shared by |
| 538 | // all instances. |
| 539 | if (instance_field_.is_static()) continue; |
| 540 | // We should only be getting const instances, which means that we |
| 541 | // should only see final instance fields. |
| 542 | ASSERT(instance_field_.is_final()); |
| 543 | tmp_string_ = instance_field_.UserVisibleName(); |
| 544 | auto const label = tmp_string_.ToCString(); |
| 545 | instance_field_value = inst.GetField(instance_field_); |
| 546 | fields->AddExtra(label, DartValueToSExp(instance_field_value)); |
| 547 | } |
| 548 | if (fields->ExtraLength() != 0 || FLAG_verbose_flow_graph_serialization) { |
| 549 | sexp->Add(fields); |
| 550 | } |
| 551 | if (instance_class.IsGeneric()) { |
| 552 | instance_type_args_ = inst.GetTypeArguments(); |
| 553 | if (auto const args = NonEmptyTypeArgumentsToSExp(instance_type_args_)) { |
| 554 | sexp->AddExtra("type_args" , args); |
| 555 | } |
| 556 | } |
| 557 | if (FLAG_verbose_flow_graph_serialization) { |
| 558 | AddExtraInteger(sexp, "size" , inst.InstanceSize()); |
| 559 | // We know the following won't call back into InstanceToSExp because we're |
| 560 | // providing it a class. |
| 561 | if (auto const cls = DartValueToSExp(instance_class)) { |
| 562 | sexp->AddExtra("class" , cls); |
| 563 | } |
| 564 | } |
| 565 | return sexp; |
| 566 | } |
| 567 | |
| 568 | SExpression* FlowGraphSerializer::FunctionToSExp(const Function& func) { |
| 569 | if (func.IsNull()) return nullptr; |
| 570 | auto const sexp = new (zone()) SExpList(zone()); |
| 571 | AddSymbol(sexp, "Function" ); |
| 572 | sexp->Add(CanonicalNameToSExp(func)); |
| 573 | if (func.IsRecognized()) { |
| 574 | AddExtraSymbol(sexp, "recognized" , |
| 575 | MethodRecognizer::KindToCString(func.recognized_kind())); |
| 576 | } |
| 577 | if (func.is_native()) { |
| 578 | tmp_string_ = func.native_name(); |
| 579 | if (!tmp_string_.IsNull()) { |
| 580 | AddExtraSymbol(sexp, "native_name" , tmp_string_.ToCString()); |
| 581 | } |
| 582 | } |
| 583 | if (func.kind() != FunctionLayout::Kind::kRegularFunction || |
| 584 | FLAG_verbose_flow_graph_serialization) { |
| 585 | AddExtraSymbol(sexp, "kind" , FunctionLayout::KindToCString(func.kind())); |
| 586 | } |
| 587 | function_type_args_ = func.type_parameters(); |
| 588 | if (auto const ta_sexp = NonEmptyTypeArgumentsToSExp(function_type_args_)) { |
| 589 | sexp->AddExtra("type_args" , ta_sexp); |
| 590 | } |
| 591 | return sexp; |
| 592 | } |
| 593 | |
| 594 | SExpression* FlowGraphSerializer::ArrayToSExp(const Array& arr) { |
| 595 | if (arr.IsNull()) return nullptr; |
| 596 | // We should only be getting immutable lists when serializing Dart values |
| 597 | // in flow graphs. |
| 598 | ASSERT(arr.IsImmutable()); |
| 599 | auto sexp = new (zone()) SExpList(zone()); |
| 600 | AddSymbol(sexp, "ImmutableList" ); |
| 601 | // We allocate a new Object handle to use for the calls to DartValueToSExp |
| 602 | // in case any Array elements contain non-constant-pool, non-empty Arrays. |
| 603 | auto& array_elem = Object::Handle(zone()); |
| 604 | for (intptr_t i = 0; i < arr.Length(); i++) { |
| 605 | array_elem = arr.At(i); |
| 606 | sexp->Add(DartValueToSExp(array_elem)); |
| 607 | } |
| 608 | array_type_args_ = arr.GetTypeArguments(); |
| 609 | if (auto const type_args_sexp = TypeArgumentsToSExp(array_type_args_)) { |
| 610 | sexp->AddExtra("type_args" , type_args_sexp); |
| 611 | } |
| 612 | return sexp; |
| 613 | } |
| 614 | |
| 615 | SExpression* FlowGraphSerializer::ClosureToSExp(const Closure& c) { |
| 616 | if (c.IsNull()) return nullptr; |
| 617 | auto sexp = new (zone()) SExpList(zone()); |
| 618 | AddSymbol(sexp, "Closure" ); |
| 619 | closure_function_ = c.function(); |
| 620 | if (auto const func = FunctionToSExp(closure_function_)) { |
| 621 | sexp->Add(func); |
| 622 | } |
| 623 | closure_context_ = c.context(); |
| 624 | if (auto const context = ContextToSExp(closure_context_)) { |
| 625 | sexp->AddExtra("context" , context); |
| 626 | } |
| 627 | closure_type_args_ = c.function_type_arguments(); |
| 628 | if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) { |
| 629 | sexp->AddExtra("func_type_args" , type_args); |
| 630 | } |
| 631 | closure_type_args_ = c.instantiator_type_arguments(); |
| 632 | if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) { |
| 633 | sexp->AddExtra("inst_type_args" , type_args); |
| 634 | } |
| 635 | closure_type_args_ = c.delayed_type_arguments(); |
| 636 | if (auto const type_args = NonEmptyTypeArgumentsToSExp(closure_type_args_)) { |
| 637 | sexp->AddExtra("delayed_type_args" , type_args); |
| 638 | } |
| 639 | return sexp; |
| 640 | } |
| 641 | |
| 642 | SExpression* FlowGraphSerializer::ContextToSExp(const Context& c) { |
| 643 | if (c.IsNull()) return nullptr; |
| 644 | auto sexp = new (zone()) SExpList(zone()); |
| 645 | AddSymbol(sexp, "Context" ); |
| 646 | for (intptr_t i = 0; i < c.num_variables(); i++) { |
| 647 | context_elem_ = c.At(i); |
| 648 | auto const elem_sexp = DartValueToSExp(context_elem_); |
| 649 | if (elem_sexp == nullptr) return nullptr; |
| 650 | sexp->Add(elem_sexp); |
| 651 | } |
| 652 | context_parent_ = c.parent(); |
| 653 | if (auto const parent_sexp = ContextToSExp(context_parent_)) { |
| 654 | sexp->AddExtra("parent" , parent_sexp); |
| 655 | } |
| 656 | return sexp; |
| 657 | } |
| 658 | |
| 659 | SExpression* FlowGraphSerializer::ObjectToSExp(const Object& dartval) { |
| 660 | if (dartval.IsNull()) { |
| 661 | return new (zone()) SExpSymbol("null" ); |
| 662 | } |
| 663 | if (dartval.raw() == Object::sentinel().raw()) { |
| 664 | return new (zone()) SExpSymbol("sentinel" ); |
| 665 | } |
| 666 | if (dartval.IsString()) { |
| 667 | return new (zone()) SExpString(dartval.ToCString()); |
| 668 | } |
| 669 | if (dartval.IsSmi()) { |
| 670 | return new (zone()) SExpInteger(Smi::Cast(dartval).Value()); |
| 671 | } |
| 672 | if (dartval.IsMint()) { |
| 673 | return new (zone()) SExpInteger(Mint::Cast(dartval).value()); |
| 674 | } |
| 675 | if (dartval.IsBool()) { |
| 676 | return new (zone()) SExpBool(Bool::Cast(dartval).value()); |
| 677 | } |
| 678 | if (dartval.IsDouble()) { |
| 679 | return new (zone()) SExpDouble(Double::Cast(dartval).value()); |
| 680 | } |
| 681 | if (dartval.IsField()) { |
| 682 | return FieldToSExp(Field::Cast(dartval)); |
| 683 | } |
| 684 | if (dartval.IsClass()) { |
| 685 | return ClassToSExp(Class::Cast(dartval)); |
| 686 | } |
| 687 | if (dartval.IsTypeArguments()) { |
| 688 | return TypeArgumentsToSExp(TypeArguments::Cast(dartval)); |
| 689 | } |
| 690 | if (dartval.IsCode()) { |
| 691 | return CodeToSExp(Code::Cast(dartval)); |
| 692 | } |
| 693 | if (dartval.IsArray()) { |
| 694 | return ArrayToSExp(Array::Cast(dartval)); |
| 695 | } |
| 696 | if (dartval.IsFunction()) { |
| 697 | return FunctionToSExp(Function::Cast(dartval)); |
| 698 | } |
| 699 | if (dartval.IsClosure()) { |
| 700 | return ClosureToSExp(Closure::Cast(dartval)); |
| 701 | } |
| 702 | if (dartval.IsAbstractType()) { |
| 703 | return AbstractTypeToSExp(AbstractType::Cast(dartval)); |
| 704 | } |
| 705 | ASSERT(dartval.IsInstance()); |
| 706 | return InstanceToSExp(Instance::Cast(dartval)); |
| 707 | } |
| 708 | |
| 709 | SExpression* FlowGraphSerializer::DartValueToSExp(const Object& obj) { |
| 710 | if (auto const def = flow_graph()->GetExistingConstant(obj)) { |
| 711 | ASSERT(def->IsDefinition()); |
| 712 | return UseToSExp(def->AsDefinition()); |
| 713 | } |
| 714 | return ObjectToSExp(obj); |
| 715 | } |
| 716 | |
| 717 | SExpression* FlowGraphSerializer::NonEmptyTypeArgumentsToSExp( |
| 718 | const TypeArguments& ta) { |
| 719 | if (ta.IsNull() || ta.Length() == 0) return nullptr; |
| 720 | return DartValueToSExp(ta); |
| 721 | } |
| 722 | |
| 723 | SExpression* FlowGraphSerializer::ConstantPoolToSExp( |
| 724 | const GraphEntryInstr* start) { |
| 725 | auto const initial_defs = start->initial_definitions(); |
| 726 | if (initial_defs == nullptr || initial_defs->is_empty()) return nullptr; |
| 727 | auto constant_list = new (zone()) SExpList(zone()); |
| 728 | AddSymbol(constant_list, "Constants" ); |
| 729 | for (intptr_t i = 0; i < initial_defs->length(); i++) { |
| 730 | ASSERT(initial_defs->At(i)->IsConstant()); |
| 731 | auto const definition = initial_defs->At(i)->AsDefinition(); |
| 732 | auto elem = new (zone()) SExpList(zone()); |
| 733 | AddSymbol(elem, "def" ); |
| 734 | elem->Add(UseToSExp(definition)); |
| 735 | // Use ObjectToSExp here, not DartValueToSExp! |
| 736 | const auto& value = definition->AsConstant()->value(); |
| 737 | elem->Add(ObjectToSExp(value)); |
| 738 | AddDefinitionExtraInfoToSExp(definition, elem); |
| 739 | // Only add constants to the LLVM constant pool that are actually used in |
| 740 | // the flow graph. |
| 741 | if (FLAG_populate_llvm_constant_pool && definition->HasUses()) { |
| 742 | auto const pool_len = llvm_constants_.Length(); |
| 743 | llvm_index_ = Smi::New(pool_len); |
| 744 | llvm_index_ ^= llvm_constant_map_.InsertOrGetValue(value, llvm_index_); |
| 745 | if (llvm_index_.Value() == pool_len) { |
| 746 | llvm_constants_.Add(value); |
| 747 | } |
| 748 | AddExtraInteger(elem, "llvm_index" , llvm_index_.Value()); |
| 749 | } |
| 750 | constant_list->Add(elem); |
| 751 | } |
| 752 | return constant_list; |
| 753 | } |
| 754 | |
| 755 | SExpression* Instruction::ToSExpression(FlowGraphSerializer* s) const { |
| 756 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 757 | s->AddSymbol(sexp, DebugName()); |
| 758 | AddOperandsToSExpression(sexp, s); |
| 759 | AddExtraInfoToSExpression(sexp, s); |
| 760 | return sexp; |
| 761 | } |
| 762 | |
| 763 | SExpression* BlockEntryInstr::ToSExpression(FlowGraphSerializer* s) const { |
| 764 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 765 | s->AddSymbol(sexp, "Block" ); |
| 766 | sexp->Add(s->BlockIdToSExp(block_id())); |
| 767 | AddOperandsToSExpression(sexp, s); |
| 768 | AddExtraInfoToSExpression(sexp, s); |
| 769 | return sexp; |
| 770 | } |
| 771 | |
| 772 | void BlockEntryInstr::(SExpList* sexp, |
| 773 | FlowGraphSerializer* s) const { |
| 774 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 775 | if (try_index() != kInvalidTryIndex) { |
| 776 | s->AddExtraInteger(sexp, "try_index" , try_index()); |
| 777 | } |
| 778 | if (auto const entry_tag = s->BlockEntryTag(this)) { |
| 779 | sexp->AddExtra("block_type" , entry_tag); |
| 780 | } |
| 781 | if (FLAG_verbose_flow_graph_serialization) { |
| 782 | if (PredecessorCount() > 0) { |
| 783 | auto const preds = new (s->zone()) SExpList(s->zone()); |
| 784 | for (intptr_t i = 0; i < PredecessorCount(); i++) { |
| 785 | preds->Add(s->BlockIdToSExp(PredecessorAt(i)->block_id())); |
| 786 | } |
| 787 | sexp->AddExtra("predecessors" , preds); |
| 788 | } |
| 789 | } |
| 790 | } |
| 791 | |
| 792 | void BlockEntryInstr::AddOperandsToSExpression(SExpList* sexp, |
| 793 | FlowGraphSerializer* s) const { |
| 794 | for (const auto* inst = next_; inst != nullptr; inst = inst->next_) { |
| 795 | sexp->Add(inst->ToSExpression(s)); |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | void JoinEntryInstr::AddOperandsToSExpression(SExpList* sexp, |
| 800 | FlowGraphSerializer* s) const { |
| 801 | if (auto phi_list = phis()) { |
| 802 | for (intptr_t i = 0; i < phi_list->length(); i++) { |
| 803 | sexp->Add(phi_list->At(i)->ToSExpression(s)); |
| 804 | } |
| 805 | } |
| 806 | BlockEntryInstr::AddOperandsToSExpression(sexp, s); |
| 807 | } |
| 808 | |
| 809 | void Instruction::AddOperandsToSExpression(SExpList* sexp, |
| 810 | FlowGraphSerializer* s) const { |
| 811 | for (intptr_t i = 0; i < InputCount(); ++i) { |
| 812 | if (InputAt(i) == nullptr) continue; |
| 813 | sexp->Add(InputAt(i)->ToSExpression(s)); |
| 814 | } |
| 815 | } |
| 816 | |
| 817 | void Instruction::(SExpList* sexp, |
| 818 | FlowGraphSerializer* s) const { |
| 819 | if (GetDeoptId() != DeoptId::kNone) { |
| 820 | s->AddExtraInteger(sexp, "deopt_id" , GetDeoptId()); |
| 821 | } |
| 822 | if (env() != nullptr) { |
| 823 | sexp->AddExtra("env" , env()->ToSExpression(s)); |
| 824 | } |
| 825 | if (!token_pos().IsNoSource()) { |
| 826 | s->AddExtraInteger(sexp, "token_pos" , token_pos().value()); |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | SExpression* Range::ToSExpression(FlowGraphSerializer* s) { |
| 831 | auto const sexp = new (s->zone()) SExpList(s->zone()); |
| 832 | s->AddSymbol(sexp, "Range" ); |
| 833 | sexp->Add(min_.ToSExpression(s)); |
| 834 | if (!max_.Equals(min_)) sexp->Add(max_.ToSExpression(s)); |
| 835 | return sexp; |
| 836 | } |
| 837 | |
| 838 | SExpression* RangeBoundary::ToSExpression(FlowGraphSerializer* s) { |
| 839 | switch (kind_) { |
| 840 | case kSymbol: { |
| 841 | auto const sexp = new (s->zone()) SExpList(s->zone()); |
| 842 | sexp->Add(s->UseToSExp(symbol())); |
| 843 | if (offset() != 0) { |
| 844 | s->AddExtraInteger(sexp, "offset" , offset()); |
| 845 | } |
| 846 | return sexp; |
| 847 | } |
| 848 | case kConstant: |
| 849 | return new (s->zone()) SExpInteger(value_); |
| 850 | default: |
| 851 | return new (s->zone()) SExpSymbol(RangeBoundary::KindToCString(kind_)); |
| 852 | } |
| 853 | } |
| 854 | |
| 855 | bool FlowGraphSerializer::(const Definition* def) { |
| 856 | return ShouldSerializeType(def->type_) || def->range() != nullptr; |
| 857 | } |
| 858 | |
| 859 | void FlowGraphSerializer::(const Definition* def, |
| 860 | SExpList* sexp) { |
| 861 | // Type() isn't a const method as it can cause changes to the type_ |
| 862 | // field, so access type_ directly instead. |
| 863 | if (ShouldSerializeType(def->type_)) { |
| 864 | sexp->AddExtra("type" , def->type_->ToSExpression(this)); |
| 865 | } |
| 866 | if (def->range() != nullptr) { |
| 867 | sexp->AddExtra("range" , def->range()->ToSExpression(this)); |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | SExpression* Definition::ToSExpression(FlowGraphSerializer* s) const { |
| 872 | // If we don't have a temp index, then this is a Definition that has no |
| 873 | // usable result. |
| 874 | const bool binds_name = HasSSATemp() || HasTemp(); |
| 875 | // Don't serialize non-binding definitions as definitions unless we either |
| 876 | // have Definition-specific extra info or we're in verbose mode. |
| 877 | if (!binds_name && !FLAG_verbose_flow_graph_serialization && |
| 878 | !s->HasDefinitionExtraInfo(this)) { |
| 879 | return Instruction::ToSExpression(s); |
| 880 | } |
| 881 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 882 | s->AddSymbol(sexp, "def" ); |
| 883 | if (binds_name) { |
| 884 | sexp->Add(s->UseToSExp(this)); |
| 885 | } else { |
| 886 | // Since there is Definition-specific extra info to serialize, we use "_" |
| 887 | // as the bound name, which lets the deserializer know the result is unused. |
| 888 | s->AddSymbol(sexp, "_" ); |
| 889 | } |
| 890 | // Add only Definition-specific extra info to this form. Any extra info |
| 891 | // that is Instruction-specific or specific to the actual instruction type is |
| 892 | // added to the nested instruction form. |
| 893 | s->AddDefinitionExtraInfoToSExp(this, sexp); |
| 894 | sexp->Add(Instruction::ToSExpression(s)); |
| 895 | return sexp; |
| 896 | } |
| 897 | |
| 898 | void AssertAssignableInstr::( |
| 899 | SExpList* sexp, |
| 900 | FlowGraphSerializer* s) const { |
| 901 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 902 | sexp->AddExtra("name" , s->DartValueToSExp(dst_name())); |
| 903 | } |
| 904 | |
| 905 | void ConstantInstr::AddOperandsToSExpression(SExpList* sexp, |
| 906 | FlowGraphSerializer* s) const { |
| 907 | sexp->Add(s->DartValueToSExp(value())); |
| 908 | } |
| 909 | |
| 910 | void BranchInstr::AddOperandsToSExpression(SExpList* sexp, |
| 911 | FlowGraphSerializer* s) const { |
| 912 | sexp->Add(comparison()->ToSExpression(s)); |
| 913 | sexp->Add(s->BlockIdToSExp(true_successor()->block_id())); |
| 914 | sexp->Add(s->BlockIdToSExp(false_successor()->block_id())); |
| 915 | } |
| 916 | |
| 917 | void ParameterInstr::AddOperandsToSExpression(SExpList* sexp, |
| 918 | FlowGraphSerializer* s) const { |
| 919 | s->AddInteger(sexp, index()); |
| 920 | s->AddExtraInteger(sexp, "param_offset" , param_offset()); |
| 921 | s->AddExtraSymbol(sexp, "representation" , |
| 922 | Location::RepresentationToCString(representation())); |
| 923 | } |
| 924 | |
| 925 | void SpecialParameterInstr::AddOperandsToSExpression( |
| 926 | SExpList* sexp, |
| 927 | FlowGraphSerializer* s) const { |
| 928 | ASSERT(kind() < SpecialParameterInstr::kNumKinds); |
| 929 | s->AddSymbol(sexp, KindToCString(kind())); |
| 930 | } |
| 931 | |
| 932 | SExpression* FlowGraphSerializer::LocalVariableToSExp(const LocalVariable& v) { |
| 933 | auto const sexp = new (zone()) SExpList(zone()); |
| 934 | AddSymbol(sexp, "LocalVariable" ); |
| 935 | if (!v.name().IsNull()) { |
| 936 | AddSymbol(sexp, v.name().ToCString()); |
| 937 | } |
| 938 | if (v.index().IsValid()) { |
| 939 | AddExtraInteger(sexp, "index" , v.index().value()); |
| 940 | } |
| 941 | return sexp; |
| 942 | } |
| 943 | |
| 944 | void LoadLocalInstr::AddOperandsToSExpression(SExpList* sexp, |
| 945 | FlowGraphSerializer* s) const { |
| 946 | sexp->Add(s->LocalVariableToSExp(local())); |
| 947 | } |
| 948 | |
| 949 | void StoreLocalInstr::AddOperandsToSExpression(SExpList* sexp, |
| 950 | FlowGraphSerializer* s) const { |
| 951 | sexp->Add(s->LocalVariableToSExp(local())); |
| 952 | } |
| 953 | |
| 954 | SExpression* FlowGraphSerializer::SlotToSExp(const Slot& slot) { |
| 955 | auto sexp = new (zone()) SExpList(zone()); |
| 956 | AddSymbol(sexp, "Slot" ); |
| 957 | AddInteger(sexp, slot.offset_in_bytes()); |
| 958 | AddExtraSymbol(sexp, "kind" , Slot::KindToCString(slot.kind())); |
| 959 | if (slot.IsDartField()) { |
| 960 | sexp->AddExtra("field" , DartValueToSExp(slot.field())); |
| 961 | } |
| 962 | return sexp; |
| 963 | } |
| 964 | |
| 965 | void LoadFieldInstr::AddOperandsToSExpression(SExpList* sexp, |
| 966 | FlowGraphSerializer* s) const { |
| 967 | sexp->Add(instance()->ToSExpression(s)); |
| 968 | sexp->Add(s->SlotToSExp(slot())); |
| 969 | } |
| 970 | |
| 971 | void LoadFieldInstr::(SExpList* sexp, |
| 972 | FlowGraphSerializer* s) const { |
| 973 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 974 | if (calls_initializer() || FLAG_verbose_flow_graph_serialization) { |
| 975 | s->AddExtraBool(sexp, "calls_initializer" , calls_initializer()); |
| 976 | } |
| 977 | } |
| 978 | |
| 979 | void StoreInstanceFieldInstr::AddOperandsToSExpression( |
| 980 | SExpList* sexp, |
| 981 | FlowGraphSerializer* s) const { |
| 982 | sexp->Add(instance()->ToSExpression(s)); |
| 983 | sexp->Add(s->SlotToSExp(slot())); |
| 984 | sexp->Add(value()->ToSExpression(s)); |
| 985 | } |
| 986 | |
| 987 | void StoreInstanceFieldInstr::( |
| 988 | SExpList* sexp, |
| 989 | FlowGraphSerializer* s) const { |
| 990 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 991 | if (is_initialization_ || FLAG_verbose_flow_graph_serialization) { |
| 992 | s->AddExtraBool(sexp, "is_init" , is_initialization_); |
| 993 | } |
| 994 | if (emit_store_barrier_ != kNoStoreBarrier || |
| 995 | FLAG_verbose_flow_graph_serialization) { |
| 996 | // Make sure that we aren't seeing a new value added to the StoreBarrierType |
| 997 | // enum that isn't handled by the serializer. |
| 998 | ASSERT(emit_store_barrier_ == kNoStoreBarrier || |
| 999 | emit_store_barrier_ == kEmitStoreBarrier); |
| 1000 | s->AddExtraBool(sexp, "emit_barrier" , |
| 1001 | emit_store_barrier_ != kNoStoreBarrier); |
| 1002 | } |
| 1003 | } |
| 1004 | |
| 1005 | void LoadIndexedUnsafeInstr::( |
| 1006 | SExpList* sexp, |
| 1007 | FlowGraphSerializer* s) const { |
| 1008 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1009 | if (offset() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1010 | s->AddExtraInteger(sexp, "offset" , offset()); |
| 1011 | } |
| 1012 | } |
| 1013 | |
| 1014 | void StoreIndexedUnsafeInstr::( |
| 1015 | SExpList* sexp, |
| 1016 | FlowGraphSerializer* s) const { |
| 1017 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1018 | if (offset() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1019 | s->AddExtraInteger(sexp, "offset" , offset()); |
| 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | void ComparisonInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1024 | FlowGraphSerializer* s) const { |
| 1025 | s->AddSymbol(sexp, Token::Str(kind())); |
| 1026 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1027 | } |
| 1028 | |
| 1029 | void StrictCompareInstr::( |
| 1030 | SExpList* sexp, |
| 1031 | FlowGraphSerializer* s) const { |
| 1032 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1033 | if (needs_number_check_ || FLAG_verbose_flow_graph_serialization) { |
| 1034 | s->AddExtraBool(sexp, "needs_check" , needs_number_check_); |
| 1035 | } |
| 1036 | } |
| 1037 | |
| 1038 | void DoubleTestOpInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1039 | FlowGraphSerializer* s) const { |
| 1040 | const bool negated = kind() != Token::kEQ; |
| 1041 | switch (op_kind()) { |
| 1042 | case MethodRecognizer::kDouble_getIsNaN: |
| 1043 | s->AddSymbol(sexp, negated ? "IsNotNaN" : "IsNaN" ); |
| 1044 | break; |
| 1045 | case MethodRecognizer::kDouble_getIsInfinite: |
| 1046 | s->AddSymbol(sexp, negated ? "IsNotInfinite" : "IsInfinite" ); |
| 1047 | break; |
| 1048 | default: |
| 1049 | UNREACHABLE(); |
| 1050 | } |
| 1051 | sexp->Add(value()->ToSExpression(s)); |
| 1052 | } |
| 1053 | |
| 1054 | void GotoInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1055 | FlowGraphSerializer* s) const { |
| 1056 | sexp->Add(s->BlockIdToSExp(successor()->block_id())); |
| 1057 | } |
| 1058 | |
| 1059 | void DebugStepCheckInstr::( |
| 1060 | SExpList* sexp, |
| 1061 | FlowGraphSerializer* s) const { |
| 1062 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1063 | if (stub_kind_ != PcDescriptorsLayout::kAnyKind || |
| 1064 | FLAG_verbose_flow_graph_serialization) { |
| 1065 | auto const stub_kind_name = PcDescriptorsLayout::KindToCString(stub_kind_); |
| 1066 | ASSERT(stub_kind_name != nullptr); |
| 1067 | s->AddExtraSymbol(sexp, "stub_kind" , stub_kind_name); |
| 1068 | } |
| 1069 | } |
| 1070 | |
| 1071 | SExpression* FlowGraphSerializer::ICDataToSExp(const ICData* ic_data) { |
| 1072 | auto const sexp = new (zone()) SExpList(zone()); |
| 1073 | AddSymbol(sexp, "ICData" ); |
| 1074 | |
| 1075 | if (ic_data->is_tracking_exactness()) { |
| 1076 | ic_data_type_ = ic_data->receivers_static_type(); |
| 1077 | sexp->AddExtra("receivers_static_type" , AbstractTypeToSExp(ic_data_type_)); |
| 1078 | } |
| 1079 | |
| 1080 | if (ic_data->is_megamorphic() || FLAG_verbose_flow_graph_serialization) { |
| 1081 | AddExtraBool(sexp, "is_megamorphic" , ic_data->is_megamorphic()); |
| 1082 | } |
| 1083 | |
| 1084 | auto const num_checks = ic_data->NumberOfChecks(); |
| 1085 | GrowableArray<intptr_t> class_ids(zone(), 2); |
| 1086 | for (intptr_t i = 0; i < num_checks; i++) { |
| 1087 | auto const entry = new (zone()) SExpList(zone()); |
| 1088 | |
| 1089 | auto const count = ic_data->GetCountAt(i); |
| 1090 | if (count > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1091 | AddExtraInteger(entry, "count" , count); |
| 1092 | } |
| 1093 | |
| 1094 | class_ids.Clear(); |
| 1095 | ic_data->GetCheckAt(i, &class_ids, &ic_data_target_); |
| 1096 | entry->AddExtra("target" , DartValueToSExp(ic_data_target_)); |
| 1097 | for (auto const cid : class_ids) { |
| 1098 | entry->Add(new (zone()) SExpInteger(cid)); |
| 1099 | } |
| 1100 | |
| 1101 | sexp->Add(entry); |
| 1102 | } |
| 1103 | |
| 1104 | if (FLAG_verbose_flow_graph_serialization) { |
| 1105 | AddExtraSymbol(sexp, "rebind_rule" , |
| 1106 | ICData::RebindRuleToCString(ic_data->rebind_rule())); |
| 1107 | tmp_string_ = ic_data->target_name(); |
| 1108 | AddExtraString(sexp, "target_name" , tmp_string_.ToCString()); |
| 1109 | ic_data_target_ = ic_data->Owner(); |
| 1110 | sexp->AddExtra("owner" , DartValueToSExp(ic_data_target_)); |
| 1111 | AddExtraInteger(sexp, "num_args_tested" , ic_data->NumArgsTested()); |
| 1112 | auto& args_desc = Array::Handle(zone(), ic_data->arguments_descriptor()); |
| 1113 | sexp->AddExtra("arguments_descriptor" , DartValueToSExp(args_desc)); |
| 1114 | } |
| 1115 | |
| 1116 | return sexp; |
| 1117 | } |
| 1118 | |
| 1119 | void TailCallInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1120 | FlowGraphSerializer* s) const { |
| 1121 | if (auto const code = s->DartValueToSExp(code_)) { |
| 1122 | sexp->Add(code); |
| 1123 | } |
| 1124 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1125 | } |
| 1126 | |
| 1127 | void NativeCallInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1128 | FlowGraphSerializer* s) const { |
| 1129 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1130 | } |
| 1131 | |
| 1132 | void NativeCallInstr::(SExpList* sexp, |
| 1133 | FlowGraphSerializer* s) const { |
| 1134 | TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s); |
| 1135 | if (auto const func = s->DartValueToSExp(function())) { |
| 1136 | sexp->AddExtra("function" , func); |
| 1137 | } |
| 1138 | if (!native_name().IsNull()) { |
| 1139 | s->AddExtraString(sexp, "name" , native_name().ToCString()); |
| 1140 | } |
| 1141 | if (link_lazily() || FLAG_verbose_flow_graph_serialization) { |
| 1142 | s->AddExtraBool(sexp, "link_lazily" , link_lazily()); |
| 1143 | } |
| 1144 | } |
| 1145 | |
| 1146 | template <intptr_t kExtraInputs> |
| 1147 | void TemplateDartCall<kExtraInputs>::( |
| 1148 | SExpList* sexp, |
| 1149 | FlowGraphSerializer* s) const { |
| 1150 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1151 | if (type_args_len() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1152 | s->AddExtraInteger(sexp, "type_args_len" , type_args_len()); |
| 1153 | } |
| 1154 | s->AddExtraInteger(sexp, "args_len" , ArgumentCountWithoutTypeArgs()); |
| 1155 | |
| 1156 | const auto& arg_names = argument_names(); |
| 1157 | if (!arg_names.IsNull()) { |
| 1158 | auto arg_names_sexp = new (s->zone()) SExpList(s->zone()); |
| 1159 | auto& str = String::Handle(s->zone()); |
| 1160 | for (intptr_t i = 0; i < arg_names.Length(); i++) { |
| 1161 | str = String::RawCast(arg_names.At(i)); |
| 1162 | arg_names_sexp->Add(s->ObjectToSExp(str)); |
| 1163 | } |
| 1164 | sexp->AddExtra("arg_names" , arg_names_sexp); |
| 1165 | } |
| 1166 | |
| 1167 | ASSERT(!HasPushArguments()); |
| 1168 | } |
| 1169 | |
| 1170 | void ClosureCallInstr::(SExpList* sexp, |
| 1171 | FlowGraphSerializer* s) const { |
| 1172 | // For now, just here to ensure TemplateDartCall<1>::AddExtraInfoToSExpression |
| 1173 | // gets instantiated. |
| 1174 | TemplateDartCall<1>::AddExtraInfoToSExpression(sexp, s); |
| 1175 | } |
| 1176 | |
| 1177 | void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1178 | FlowGraphSerializer* s) const { |
| 1179 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1180 | } |
| 1181 | |
| 1182 | void StaticCallInstr::(SExpList* sexp, |
| 1183 | FlowGraphSerializer* s) const { |
| 1184 | TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s); |
| 1185 | |
| 1186 | if (auto const func = s->DartValueToSExp(function())) { |
| 1187 | sexp->AddExtra("function" , func); |
| 1188 | } |
| 1189 | |
| 1190 | if (HasICData()) { |
| 1191 | sexp->AddExtra("ic_data" , s->ICDataToSExp(ic_data())); |
| 1192 | } else if (CallCount() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1193 | s->AddExtraInteger(sexp, "call_count" , CallCount()); |
| 1194 | } |
| 1195 | |
| 1196 | if (rebind_rule_ != ICData::kStatic || |
| 1197 | FLAG_verbose_flow_graph_serialization) { |
| 1198 | auto const str = ICData::RebindRuleToCString(rebind_rule_); |
| 1199 | ASSERT(str != nullptr); |
| 1200 | s->AddExtraSymbol(sexp, "rebind_rule" , str); |
| 1201 | } |
| 1202 | |
| 1203 | if (ShouldSerializeType(result_type())) { |
| 1204 | sexp->AddExtra("result_type" , result_type()->ToSExpression(s)); |
| 1205 | } |
| 1206 | |
| 1207 | if (entry_kind() != Code::EntryKind::kNormal || |
| 1208 | FLAG_verbose_flow_graph_serialization) { |
| 1209 | auto const kind_str = Code::EntryKindToCString(entry_kind()); |
| 1210 | s->AddExtraSymbol(sexp, "entry_kind" , kind_str); |
| 1211 | } |
| 1212 | } |
| 1213 | |
| 1214 | void InstanceCallBaseInstr::AddOperandsToSExpression( |
| 1215 | SExpList* sexp, |
| 1216 | FlowGraphSerializer* s) const { |
| 1217 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1218 | } |
| 1219 | |
| 1220 | void InstanceCallBaseInstr::( |
| 1221 | SExpList* sexp, |
| 1222 | FlowGraphSerializer* s) const { |
| 1223 | TemplateDartCall<0>::AddExtraInfoToSExpression(sexp, s); |
| 1224 | |
| 1225 | if (auto const target = s->DartValueToSExp(interface_target())) { |
| 1226 | sexp->AddExtra("interface_target" , target); |
| 1227 | } |
| 1228 | |
| 1229 | if (auto const target = s->DartValueToSExp(tearoff_interface_target())) { |
| 1230 | sexp->AddExtra("tearoff_interface_target" , target); |
| 1231 | } |
| 1232 | |
| 1233 | if (HasICData()) { |
| 1234 | sexp->AddExtra("ic_data" , s->ICDataToSExp(ic_data())); |
| 1235 | } |
| 1236 | |
| 1237 | if (function_name().IsNull()) { |
| 1238 | if (!interface_target().IsNull() || !tearoff_interface_target().IsNull()) { |
| 1239 | s->AddExtraSymbol(sexp, "function_name" , "null" ); |
| 1240 | } |
| 1241 | } else { |
| 1242 | if (interface_target().IsNull() || |
| 1243 | (function_name().raw() != interface_target().name() && |
| 1244 | function_name().raw() != tearoff_interface_target().name())) { |
| 1245 | s->AddExtraString(sexp, "function_name" , function_name().ToCString()); |
| 1246 | } |
| 1247 | } |
| 1248 | |
| 1249 | if (token_kind() != Token::kILLEGAL) { |
| 1250 | s->AddExtraSymbol(sexp, "token_kind" , Token::Str(token_kind())); |
| 1251 | } |
| 1252 | |
| 1253 | if (ShouldSerializeType(result_type())) { |
| 1254 | sexp->AddExtra("result_type" , result_type()->ToSExpression(s)); |
| 1255 | } |
| 1256 | |
| 1257 | if (entry_kind() != Code::EntryKind::kNormal || |
| 1258 | FLAG_verbose_flow_graph_serialization) { |
| 1259 | auto const kind_str = Code::EntryKindToCString(entry_kind()); |
| 1260 | s->AddExtraSymbol(sexp, "entry_kind" , kind_str); |
| 1261 | } |
| 1262 | } |
| 1263 | |
| 1264 | void InstanceCallInstr::( |
| 1265 | SExpList* sexp, |
| 1266 | FlowGraphSerializer* s) const { |
| 1267 | InstanceCallBaseInstr::AddExtraInfoToSExpression(sexp, s); |
| 1268 | |
| 1269 | if (checked_argument_count() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1270 | s->AddExtraInteger(sexp, "checked_arg_count" , checked_argument_count()); |
| 1271 | } |
| 1272 | } |
| 1273 | |
| 1274 | void PolymorphicInstanceCallInstr::( |
| 1275 | SExpList* sexp, |
| 1276 | FlowGraphSerializer* s) const { |
| 1277 | InstanceCallBaseInstr::AddExtraInfoToSExpression(sexp, s); |
| 1278 | |
| 1279 | if (targets().length() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1280 | auto elem_list = new (s->zone()) SExpList(s->zone()); |
| 1281 | for (intptr_t i = 0; i < targets().length(); i++) { |
| 1282 | auto elem = new (s->zone()) SExpList(s->zone()); |
| 1283 | const TargetInfo* ti = targets().TargetAt(i); |
| 1284 | if (ti->cid_start == ti->cid_end) { |
| 1285 | s->AddInteger(elem, ti->cid_start); |
| 1286 | } else { |
| 1287 | auto range = new (s->zone()) SExpList(s->zone()); |
| 1288 | s->AddInteger(range, ti->cid_start); |
| 1289 | s->AddInteger(range, ti->cid_end); |
| 1290 | elem->Add(range); |
| 1291 | } |
| 1292 | if (auto const target = s->DartValueToSExp(*ti->target)) { |
| 1293 | elem->Add(target); |
| 1294 | } |
| 1295 | elem_list->Add(elem); |
| 1296 | } |
| 1297 | sexp->AddExtra("targets" , elem_list); |
| 1298 | } |
| 1299 | } |
| 1300 | |
| 1301 | void AllocateObjectInstr::AddOperandsToSExpression( |
| 1302 | SExpList* sexp, |
| 1303 | FlowGraphSerializer* s) const { |
| 1304 | if (auto const sexp_cls = s->DartValueToSExp(cls())) { |
| 1305 | sexp->Add(sexp_cls); |
| 1306 | } |
| 1307 | if (type_arguments() != nullptr) { |
| 1308 | sexp->Add(type_arguments()->ToSExpression(s)); |
| 1309 | } |
| 1310 | } |
| 1311 | |
| 1312 | void AllocateObjectInstr::( |
| 1313 | SExpList* sexp, |
| 1314 | FlowGraphSerializer* s) const { |
| 1315 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1316 | s->AddExtraInteger(sexp, "size" , cls().target_instance_size()); |
| 1317 | if (auto const closure = s->DartValueToSExp(closure_function())) { |
| 1318 | sexp->AddExtra("closure_function" , closure); |
| 1319 | } |
| 1320 | if (!Identity().IsUnknown() || FLAG_verbose_flow_graph_serialization) { |
| 1321 | s->AddExtraSymbol(sexp, "identity" , Identity().ToCString()); |
| 1322 | } |
| 1323 | } |
| 1324 | |
| 1325 | void BinaryIntegerOpInstr::AddOperandsToSExpression( |
| 1326 | SExpList* sexp, |
| 1327 | FlowGraphSerializer* s) const { |
| 1328 | s->AddSymbol(sexp, Token::Str(op_kind())); |
| 1329 | sexp->Add(left()->ToSExpression(s)); |
| 1330 | sexp->Add(right()->ToSExpression(s)); |
| 1331 | } |
| 1332 | |
| 1333 | void CheckedSmiOpInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1334 | FlowGraphSerializer* s) const { |
| 1335 | s->AddSymbol(sexp, Token::Str(op_kind())); |
| 1336 | sexp->Add(left()->ToSExpression(s)); |
| 1337 | sexp->Add(right()->ToSExpression(s)); |
| 1338 | } |
| 1339 | |
| 1340 | // clang-format off |
| 1341 | static const char* simd_op_kind_string[] = { |
| 1342 | #define CASE(Arity, Mask, Name, ...) #Name, |
| 1343 | SIMD_OP_LIST(CASE, CASE) |
| 1344 | #undef CASE |
| 1345 | "IllegalSimdOp" , |
| 1346 | }; |
| 1347 | // clang-format on |
| 1348 | |
| 1349 | void SimdOpInstr::AddOperandsToSExpression(SExpList* sexp, |
| 1350 | FlowGraphSerializer* s) const { |
| 1351 | s->AddSymbol(sexp, simd_op_kind_string[kind()]); |
| 1352 | Instruction::AddOperandsToSExpression(sexp, s); |
| 1353 | } |
| 1354 | |
| 1355 | void SimdOpInstr::(SExpList* sexp, |
| 1356 | FlowGraphSerializer* s) const { |
| 1357 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1358 | if (HasMask()) { |
| 1359 | s->AddExtraInteger(sexp, "mask" , mask()); |
| 1360 | } |
| 1361 | } |
| 1362 | |
| 1363 | void LoadIndexedInstr::(SExpList* sexp, |
| 1364 | FlowGraphSerializer* s) const { |
| 1365 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1366 | if (aligned() || FLAG_verbose_flow_graph_serialization) { |
| 1367 | s->AddExtraBool(sexp, "aligned" , aligned()); |
| 1368 | } |
| 1369 | if (index_scale() > 1 || FLAG_verbose_flow_graph_serialization) { |
| 1370 | s->AddExtraInteger(sexp, "scale" , index_scale()); |
| 1371 | } |
| 1372 | if (class_id() != kDynamicCid || FLAG_verbose_flow_graph_serialization) { |
| 1373 | s->AddExtraInteger(sexp, "cid" , class_id()); |
| 1374 | } |
| 1375 | } |
| 1376 | |
| 1377 | void StoreIndexedInstr::( |
| 1378 | SExpList* sexp, |
| 1379 | FlowGraphSerializer* s) const { |
| 1380 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1381 | if (aligned() || FLAG_verbose_flow_graph_serialization) { |
| 1382 | s->AddExtraBool(sexp, "aligned" , aligned()); |
| 1383 | } |
| 1384 | if (index_scale() > 1 || FLAG_verbose_flow_graph_serialization) { |
| 1385 | s->AddExtraInteger(sexp, "scale" , index_scale()); |
| 1386 | } |
| 1387 | if (class_id() != kDynamicCid || FLAG_verbose_flow_graph_serialization) { |
| 1388 | s->AddExtraInteger(sexp, "cid" , class_id()); |
| 1389 | } |
| 1390 | } |
| 1391 | |
| 1392 | void CheckStackOverflowInstr::( |
| 1393 | SExpList* sexp, |
| 1394 | FlowGraphSerializer* s) const { |
| 1395 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1396 | if (stack_depth() > 0 || FLAG_verbose_flow_graph_serialization) { |
| 1397 | s->AddExtraInteger(sexp, "stack_depth" , stack_depth()); |
| 1398 | } |
| 1399 | if (in_loop() || FLAG_verbose_flow_graph_serialization) { |
| 1400 | s->AddExtraInteger(sexp, "loop_depth" , loop_depth()); |
| 1401 | } |
| 1402 | if (kind_ != kOsrAndPreemption) { |
| 1403 | ASSERT(kind_ == kOsrOnly); |
| 1404 | s->AddExtraSymbol(sexp, "kind" , "OsrOnly" ); |
| 1405 | } |
| 1406 | } |
| 1407 | |
| 1408 | void CheckNullInstr::(SExpList* sexp, |
| 1409 | FlowGraphSerializer* s) const { |
| 1410 | Instruction::AddExtraInfoToSExpression(sexp, s); |
| 1411 | if (!function_name_.IsNull()) { |
| 1412 | s->AddExtraString(sexp, "function_name" , function_name_.ToCString()); |
| 1413 | } |
| 1414 | } |
| 1415 | |
| 1416 | SExpression* Value::ToSExpression(FlowGraphSerializer* s) const { |
| 1417 | auto name = s->UseToSExp(definition()); |
| 1418 | // If we're not serializing types or there is no reaching type for this use, |
| 1419 | // just serialize the use as the bound name. |
| 1420 | if (!ShouldSerializeType(reaching_type_)) return name; |
| 1421 | |
| 1422 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 1423 | s->AddSymbol(sexp, "value" ); |
| 1424 | sexp->Add(name); |
| 1425 | // If there is no owner for the type, then serialize the type in full. |
| 1426 | // Otherwise the owner should be the definition, so we'll inherit the type |
| 1427 | // from it. (That is, (value v<X>) with no explicit type info means the |
| 1428 | // reaching type comes from the definition of v<X>.) We'll serialize an |
| 1429 | // "inherit_type" extra info field to make this explicit when in verbose mode. |
| 1430 | if (reaching_type_->owner() == nullptr) { |
| 1431 | sexp->AddExtra("type" , reaching_type_->ToSExpression(s)); |
| 1432 | } else { |
| 1433 | ASSERT(reaching_type_->owner() == definition()); |
| 1434 | } |
| 1435 | if (FLAG_verbose_flow_graph_serialization) { |
| 1436 | s->AddExtraBool(sexp, "inherit_type" , |
| 1437 | reaching_type_->owner() == definition()); |
| 1438 | } |
| 1439 | return sexp; |
| 1440 | } |
| 1441 | |
| 1442 | SExpression* CompileType::ToSExpression(FlowGraphSerializer* s) const { |
| 1443 | ASSERT(FLAG_verbose_flow_graph_serialization || |
| 1444 | FLAG_serialize_flow_graph_types); |
| 1445 | |
| 1446 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 1447 | s->AddSymbol(sexp, "CompileType" ); |
| 1448 | AddExtraInfoToSExpression(sexp, s); |
| 1449 | return sexp; |
| 1450 | } |
| 1451 | |
| 1452 | void CompileType::(SExpList* sexp, |
| 1453 | FlowGraphSerializer* s) const { |
| 1454 | if (cid_ != kIllegalCid || FLAG_verbose_flow_graph_serialization) { |
| 1455 | s->AddExtraInteger(sexp, "cid" , cid_); |
| 1456 | } |
| 1457 | // TODO(sstrickl): Currently we only print out nullable if it's false |
| 1458 | // (or during verbose printing). Switch this when NNBD is the standard. |
| 1459 | if (!is_nullable() || FLAG_verbose_flow_graph_serialization) { |
| 1460 | s->AddExtraBool(sexp, "nullable" , is_nullable()); |
| 1461 | } |
| 1462 | if (type_ != nullptr) { |
| 1463 | sexp->AddExtra("type" , s->DartValueToSExp(*type_)); |
| 1464 | } |
| 1465 | } |
| 1466 | |
| 1467 | SExpression* Environment::ToSExpression(FlowGraphSerializer* s) const { |
| 1468 | auto sexp = new (s->zone()) SExpList(s->zone()); |
| 1469 | for (intptr_t i = 0; i < values_.length(); ++i) { |
| 1470 | ASSERT(!values_[i]->definition()->IsPushArgument()); |
| 1471 | sexp->Add(values_[i]->ToSExpression(s)); |
| 1472 | // TODO(sstrickl): This currently assumes that there are no locations in the |
| 1473 | // environment (e.g. before register allocation). If we ever want to print |
| 1474 | // out environments on steps after AllocateRegisters, we'll need to handle |
| 1475 | // locations as well. |
| 1476 | ASSERT(locations_ == nullptr || locations_[i].IsInvalid()); |
| 1477 | } |
| 1478 | if (outer_ != NULL) { |
| 1479 | auto outer_sexp = outer_->ToSExpression(s)->AsList(); |
| 1480 | if (outer_->deopt_id_ != DeoptId::kNone) { |
| 1481 | s->AddExtraInteger(outer_sexp, "deopt_id" , outer_->deopt_id_); |
| 1482 | } |
| 1483 | sexp->AddExtra("outer" , outer_sexp); |
| 1484 | } |
| 1485 | return sexp; |
| 1486 | } |
| 1487 | |
| 1488 | } // namespace dart |
| 1489 | |