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 | |