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
15namespace dart {
16
17DEFINE_FLAG(bool,
18 serialize_flow_graph_types,
19 true,
20 "Serialize inferred type information in flow graphs");
21
22DEFINE_FLAG(bool,
23 verbose_flow_graph_serialization,
24 false,
25 "Serialize extra information useful for debugging");
26
27DEFINE_FLAG(bool,
28 pretty_print_serialization,
29 false,
30 "Format serialized output nicely");
31
32DECLARE_FLAG(bool, populate_llvm_constant_pool);
33
34const char* const FlowGraphSerializer::initial_indent = "";
35
36FlowGraphSerializer::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
75FlowGraphSerializer::~FlowGraphSerializer() {
76 object_store_->set_llvm_constant_hash_table(llvm_constant_map_.Release());
77}
78
79void 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
92SExpression* 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,
99static const char* block_entry_kind_tags[FlowGraphSerializer::kNumEntryKinds] =
100 {FOR_EACH_BLOCK_ENTRY_KIND(KIND_STR)};
101#undef KIND_STR
102
103FlowGraphSerializer::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
114void FlowGraphSerializer::AddBool(SExpList* sexp, bool b) {
115 sexp->Add(new (zone()) SExpBool(b));
116}
117
118void FlowGraphSerializer::AddInteger(SExpList* sexp, intptr_t i) {
119 sexp->Add(new (zone()) SExpInteger(i));
120}
121
122void FlowGraphSerializer::AddString(SExpList* sexp, const char* cstr) {
123 sexp->Add(new (zone()) SExpString(cstr));
124}
125
126void FlowGraphSerializer::AddSymbol(SExpList* sexp, const char* cstr) {
127 sexp->Add(new (zone()) SExpSymbol(cstr));
128}
129
130void FlowGraphSerializer::AddExtraBool(SExpList* sexp,
131 const char* label,
132 bool b) {
133 sexp->AddExtra(label, new (zone()) SExpBool(b));
134}
135
136void FlowGraphSerializer::AddExtraInteger(SExpList* sexp,
137 const char* label,
138 intptr_t i) {
139 sexp->AddExtra(label, new (zone()) SExpInteger(i));
140}
141
142void FlowGraphSerializer::AddExtraString(SExpList* sexp,
143 const char* label,
144 const char* cstr) {
145 sexp->AddExtra(label, new (zone()) SExpString(cstr));
146}
147
148void FlowGraphSerializer::AddExtraSymbol(SExpList* sexp,
149 const char* label,
150 const char* cstr) {
151 sexp->AddExtra(label, new (zone()) SExpSymbol(cstr));
152}
153
154SExpression* FlowGraphSerializer::BlockIdToSExp(intptr_t block_id) {
155 return new (zone()) SExpSymbol(OS::SCreate(zone(), "B%" Pd "", block_id));
156}
157
158void 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
216SExpression* 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
223SExpSymbol* 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]
229SExpSymbol* 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
258SExpression* 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
278SExpression* 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
299SExpression* 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
331SExpression* 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
352SExpression* 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
367static bool ShouldSerializeType(CompileType* type) {
368 return (FLAG_verbose_flow_graph_serialization ||
369 FLAG_serialize_flow_graph_types) &&
370 type != nullptr;
371}
372
373SExpression* 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
385SExpression* 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
478SExpression* 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
504SExpression* 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
518SExpression* 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
568SExpression* 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
594SExpression* 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
615SExpression* 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
642SExpression* 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
659SExpression* 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
709SExpression* 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
717SExpression* FlowGraphSerializer::NonEmptyTypeArgumentsToSExp(
718 const TypeArguments& ta) {
719 if (ta.IsNull() || ta.Length() == 0) return nullptr;
720 return DartValueToSExp(ta);
721}
722
723SExpression* 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
755SExpression* 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
763SExpression* 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
772void BlockEntryInstr::AddExtraInfoToSExpression(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
792void 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
799void 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
809void 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
817void Instruction::AddExtraInfoToSExpression(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
830SExpression* 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
838SExpression* 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
855bool FlowGraphSerializer::HasDefinitionExtraInfo(const Definition* def) {
856 return ShouldSerializeType(def->type_) || def->range() != nullptr;
857}
858
859void FlowGraphSerializer::AddDefinitionExtraInfoToSExp(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
871SExpression* 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
898void AssertAssignableInstr::AddExtraInfoToSExpression(
899 SExpList* sexp,
900 FlowGraphSerializer* s) const {
901 Instruction::AddExtraInfoToSExpression(sexp, s);
902 sexp->AddExtra("name", s->DartValueToSExp(dst_name()));
903}
904
905void ConstantInstr::AddOperandsToSExpression(SExpList* sexp,
906 FlowGraphSerializer* s) const {
907 sexp->Add(s->DartValueToSExp(value()));
908}
909
910void 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
917void 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
925void SpecialParameterInstr::AddOperandsToSExpression(
926 SExpList* sexp,
927 FlowGraphSerializer* s) const {
928 ASSERT(kind() < SpecialParameterInstr::kNumKinds);
929 s->AddSymbol(sexp, KindToCString(kind()));
930}
931
932SExpression* 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
944void LoadLocalInstr::AddOperandsToSExpression(SExpList* sexp,
945 FlowGraphSerializer* s) const {
946 sexp->Add(s->LocalVariableToSExp(local()));
947}
948
949void StoreLocalInstr::AddOperandsToSExpression(SExpList* sexp,
950 FlowGraphSerializer* s) const {
951 sexp->Add(s->LocalVariableToSExp(local()));
952}
953
954SExpression* 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
965void LoadFieldInstr::AddOperandsToSExpression(SExpList* sexp,
966 FlowGraphSerializer* s) const {
967 sexp->Add(instance()->ToSExpression(s));
968 sexp->Add(s->SlotToSExp(slot()));
969}
970
971void LoadFieldInstr::AddExtraInfoToSExpression(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
979void 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
987void StoreInstanceFieldInstr::AddExtraInfoToSExpression(
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
1005void LoadIndexedUnsafeInstr::AddExtraInfoToSExpression(
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
1014void StoreIndexedUnsafeInstr::AddExtraInfoToSExpression(
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
1023void ComparisonInstr::AddOperandsToSExpression(SExpList* sexp,
1024 FlowGraphSerializer* s) const {
1025 s->AddSymbol(sexp, Token::Str(kind()));
1026 Instruction::AddOperandsToSExpression(sexp, s);
1027}
1028
1029void StrictCompareInstr::AddExtraInfoToSExpression(
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
1038void 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
1054void GotoInstr::AddOperandsToSExpression(SExpList* sexp,
1055 FlowGraphSerializer* s) const {
1056 sexp->Add(s->BlockIdToSExp(successor()->block_id()));
1057}
1058
1059void DebugStepCheckInstr::AddExtraInfoToSExpression(
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
1071SExpression* 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
1119void 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
1127void NativeCallInstr::AddOperandsToSExpression(SExpList* sexp,
1128 FlowGraphSerializer* s) const {
1129 Instruction::AddOperandsToSExpression(sexp, s);
1130}
1131
1132void NativeCallInstr::AddExtraInfoToSExpression(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
1146template <intptr_t kExtraInputs>
1147void TemplateDartCall<kExtraInputs>::AddExtraInfoToSExpression(
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
1170void ClosureCallInstr::AddExtraInfoToSExpression(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
1177void StaticCallInstr::AddOperandsToSExpression(SExpList* sexp,
1178 FlowGraphSerializer* s) const {
1179 Instruction::AddOperandsToSExpression(sexp, s);
1180}
1181
1182void StaticCallInstr::AddExtraInfoToSExpression(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
1214void InstanceCallBaseInstr::AddOperandsToSExpression(
1215 SExpList* sexp,
1216 FlowGraphSerializer* s) const {
1217 Instruction::AddOperandsToSExpression(sexp, s);
1218}
1219
1220void InstanceCallBaseInstr::AddExtraInfoToSExpression(
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
1264void InstanceCallInstr::AddExtraInfoToSExpression(
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
1274void PolymorphicInstanceCallInstr::AddExtraInfoToSExpression(
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
1301void 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
1312void AllocateObjectInstr::AddExtraInfoToSExpression(
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
1325void 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
1333void 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
1341static 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
1349void SimdOpInstr::AddOperandsToSExpression(SExpList* sexp,
1350 FlowGraphSerializer* s) const {
1351 s->AddSymbol(sexp, simd_op_kind_string[kind()]);
1352 Instruction::AddOperandsToSExpression(sexp, s);
1353}
1354
1355void SimdOpInstr::AddExtraInfoToSExpression(SExpList* sexp,
1356 FlowGraphSerializer* s) const {
1357 Instruction::AddExtraInfoToSExpression(sexp, s);
1358 if (HasMask()) {
1359 s->AddExtraInteger(sexp, "mask", mask());
1360 }
1361}
1362
1363void LoadIndexedInstr::AddExtraInfoToSExpression(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
1377void StoreIndexedInstr::AddExtraInfoToSExpression(
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
1392void CheckStackOverflowInstr::AddExtraInfoToSExpression(
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
1408void CheckNullInstr::AddExtraInfoToSExpression(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
1416SExpression* 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
1442SExpression* 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
1452void CompileType::AddExtraInfoToSExpression(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
1467SExpression* 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