1// Copyright (c) 2017, 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/compilation_trace.h"
6
7#include "vm/compiler/jit/compiler.h"
8#include "vm/globals.h"
9#include "vm/log.h"
10#include "vm/longjump.h"
11#include "vm/object_store.h"
12#include "vm/resolver.h"
13#include "vm/symbols.h"
14#include "vm/version.h"
15
16namespace dart {
17
18#if !defined(DART_PRECOMPILED_RUNTIME)
19
20DEFINE_FLAG(bool, trace_compilation_trace, false, "Trace compilation trace.");
21
22CompilationTraceSaver::CompilationTraceSaver(Zone* zone)
23 : buf_(zone, 1 * MB),
24 func_name_(String::Handle(zone)),
25 cls_(Class::Handle(zone)),
26 cls_name_(String::Handle(zone)),
27 lib_(Library::Handle(zone)),
28 uri_(String::Handle(zone)) {}
29
30void CompilationTraceSaver::VisitFunction(const Function& function) {
31 if (!function.HasCode()) {
32 return; // Not compiled.
33 }
34 if (function.parent_function() != Function::null()) {
35 // Lookup works poorly for local functions. We compile all local functions
36 // in a compiled function instead.
37 return;
38 }
39
40 func_name_ = function.name();
41 func_name_ = String::RemovePrivateKey(func_name_);
42 cls_ = function.Owner();
43 cls_name_ = cls_.Name();
44 cls_name_ = String::RemovePrivateKey(cls_name_);
45 lib_ = cls_.library();
46 uri_ = lib_.url();
47 buf_.Printf("%s,%s,%s\n", uri_.ToCString(), cls_name_.ToCString(),
48 func_name_.ToCString());
49}
50
51CompilationTraceLoader::CompilationTraceLoader(Thread* thread)
52 : thread_(thread),
53 zone_(thread->zone()),
54 uri_(String::Handle(zone_)),
55 class_name_(String::Handle(zone_)),
56 function_name_(String::Handle(zone_)),
57 function_name2_(String::Handle(zone_)),
58 lib_(Library::Handle(zone_)),
59 cls_(Class::Handle(zone_)),
60 function_(Function::Handle(zone_)),
61 function2_(Function::Handle(zone_)),
62 field_(Field::Handle(zone_)),
63 sites_(Array::Handle(zone_)),
64 site_(ICData::Handle(zone_)),
65 static_type_(AbstractType::Handle(zone_)),
66 receiver_cls_(Class::Handle(zone_)),
67 target_(Function::Handle(zone_)),
68 selector_(String::Handle(zone_)),
69 args_desc_(Array::Handle(zone_)),
70 error_(Object::Handle(zone_)) {}
71
72static char* FindCharacter(char* str, char goal, char* limit) {
73 while (str < limit) {
74 if (*str == goal) {
75 return str;
76 }
77 str++;
78 }
79 return NULL;
80}
81
82ObjectPtr CompilationTraceLoader::CompileTrace(uint8_t* buffer, intptr_t size) {
83 // First compile functions named in the trace.
84 char* cursor = reinterpret_cast<char*>(buffer);
85 char* limit = cursor + size;
86 while (cursor < limit) {
87 char* uri = cursor;
88 char* comma1 = FindCharacter(uri, ',', limit);
89 if (comma1 == NULL) {
90 break;
91 }
92 *comma1 = 0;
93 char* cls_name = comma1 + 1;
94 char* comma2 = FindCharacter(cls_name, ',', limit);
95 if (comma2 == NULL) {
96 break;
97 }
98 *comma2 = 0;
99 char* func_name = comma2 + 1;
100 char* newline = FindCharacter(func_name, '\n', limit);
101 if (newline == NULL) {
102 break;
103 }
104 *newline = 0;
105 error_ = CompileTriple(uri, cls_name, func_name);
106 if (error_.IsError()) {
107 return error_.raw();
108 }
109 cursor = newline + 1;
110 }
111
112 // Next, compile common dispatchers. These aren't found with the normal
113 // lookup above because they have irregular lookup that depends on the
114 // arguments descriptor (e.g. call() versus call(x)).
115 const Class& closure_class =
116 Class::Handle(zone_, thread_->isolate()->object_store()->closure_class());
117 Array& arguments_descriptor = Array::Handle(zone_);
118 Function& dispatcher = Function::Handle(zone_);
119 for (intptr_t argc = 1; argc <= 4; argc++) {
120 const intptr_t kTypeArgsLen = 0;
121
122 // TODO(dartbug.com/33549): Update this code to use the size of the
123 // parameters when supporting calls to closures with unboxed parameters.
124 arguments_descriptor = ArgumentsDescriptor::NewBoxed(kTypeArgsLen, argc);
125 dispatcher = closure_class.GetInvocationDispatcher(
126 Symbols::Call(), arguments_descriptor,
127 FunctionLayout::kInvokeFieldDispatcher, true /* create_if_absent */);
128 error_ = CompileFunction(dispatcher);
129 if (error_.IsError()) {
130 return error_.raw();
131 }
132 }
133
134 // Finally, compile closures in all compiled functions. Don't cache the
135 // length since compiling may append to this list.
136 const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
137 zone_, thread_->isolate()->object_store()->closure_functions());
138 for (intptr_t i = 0; i < closure_functions.Length(); i++) {
139 function_ ^= closure_functions.At(i);
140 function2_ = function_.parent_function();
141 if (function2_.HasCode()) {
142 error_ = CompileFunction(function_);
143 if (error_.IsError()) {
144 return error_.raw();
145 }
146 }
147 }
148
149 return Object::null();
150}
151
152// Use a fuzzy match to find the right function to compile. This allows a
153// compilation trace to remain mostly valid in the face of program changes, and
154// deals with implicit/dispatcher functions that don't have proper names.
155// - Ignore private name mangling
156// - If looking for a getter and we only have the corresponding regular method,
157// compile the regular method, create its implicit closure and compile that.
158// - If looking for a regular method and we only have the corresponding getter,
159// compile the getter, create its method extractor and compile that.
160// - If looking for a getter and we only have a const field, evaluate the const
161// field.
162ObjectPtr CompilationTraceLoader::CompileTriple(const char* uri_cstr,
163 const char* cls_cstr,
164 const char* func_cstr) {
165 uri_ = Symbols::New(thread_, uri_cstr);
166 class_name_ = Symbols::New(thread_, cls_cstr);
167 function_name_ = Symbols::New(thread_, func_cstr);
168
169 if (function_name_.Equals("_getMainClosure")) {
170 // The scheme for invoking main relies on compiling _getMainClosure after
171 // synthetically importing the root library.
172 if (FLAG_trace_compilation_trace) {
173 THR_Print("Compilation trace: skip %s,%s,%s\n", uri_.ToCString(),
174 class_name_.ToCString(), function_name_.ToCString());
175 }
176 return Object::null();
177 }
178
179 lib_ = Library::LookupLibrary(thread_, uri_);
180 if (lib_.IsNull()) {
181 // Missing library.
182 if (FLAG_trace_compilation_trace) {
183 THR_Print("Compilation trace: missing library %s,%s,%s\n",
184 uri_.ToCString(), class_name_.ToCString(),
185 function_name_.ToCString());
186 }
187 return Object::null();
188 }
189
190 bool is_dyn = Function::IsDynamicInvocationForwarderName(function_name_);
191 if (is_dyn) {
192 function_name_ =
193 Function::DemangleDynamicInvocationForwarderName(function_name_);
194 }
195
196 bool is_getter = Field::IsGetterName(function_name_);
197 bool is_init = Field::IsInitName(function_name_);
198 bool add_closure = false;
199 bool processed = false;
200
201 if (class_name_.Equals(Symbols::TopLevel())) {
202 function_ = lib_.LookupFunctionAllowPrivate(function_name_);
203 field_ = lib_.LookupFieldAllowPrivate(function_name_);
204 if (function_.IsNull() && is_getter) {
205 // Maybe this was a tear off.
206 add_closure = true;
207 function_name2_ = Field::NameFromGetter(function_name_);
208 function_ = lib_.LookupFunctionAllowPrivate(function_name2_);
209 field_ = lib_.LookupFieldAllowPrivate(function_name2_);
210 }
211 if (field_.IsNull() && is_getter) {
212 function_name2_ = Field::NameFromGetter(function_name_);
213 field_ = lib_.LookupFieldAllowPrivate(function_name2_);
214 }
215 if (field_.IsNull() && is_init) {
216 function_name2_ = Field::NameFromInit(function_name_);
217 field_ = lib_.LookupFieldAllowPrivate(function_name2_);
218 }
219 } else {
220 cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(class_name_);
221 if (cls_.IsNull()) {
222 // Missing class.
223 if (FLAG_trace_compilation_trace) {
224 THR_Print("Compilation trace: missing class %s,%s,%s\n",
225 uri_.ToCString(), class_name_.ToCString(),
226 function_name_.ToCString());
227 }
228 return Object::null();
229 }
230
231 error_ = cls_.EnsureIsFinalized(thread_);
232 if (error_.IsError()) {
233 // Non-finalized class.
234 if (FLAG_trace_compilation_trace) {
235 THR_Print("Compilation trace: non-finalized class %s,%s,%s (%s)\n",
236 uri_.ToCString(), class_name_.ToCString(),
237 function_name_.ToCString(),
238 Error::Cast(error_).ToErrorCString());
239 }
240 return error_.raw();
241 }
242
243 function_ = cls_.LookupFunctionAllowPrivate(function_name_);
244 field_ = cls_.LookupFieldAllowPrivate(function_name_);
245 if (function_.IsNull() && is_getter) {
246 // Maybe this was a tear off.
247 add_closure = true;
248 function_name2_ = Field::NameFromGetter(function_name_);
249 function_ = cls_.LookupFunctionAllowPrivate(function_name2_);
250 field_ = cls_.LookupFieldAllowPrivate(function_name2_);
251 if (!function_.IsNull() && !function_.is_static()) {
252 // Maybe this was a method extractor.
253 function2_ =
254 Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_);
255 if (!function2_.IsNull()) {
256 error_ = CompileFunction(function2_);
257 if (error_.IsError()) {
258 if (FLAG_trace_compilation_trace) {
259 THR_Print(
260 "Compilation trace: error compiling extractor %s for "
261 "%s,%s,%s (%s)\n",
262 function2_.ToCString(), uri_.ToCString(),
263 class_name_.ToCString(), function_name_.ToCString(),
264 Error::Cast(error_).ToErrorCString());
265 }
266 return error_.raw();
267 }
268 }
269 }
270 }
271 if (field_.IsNull() && is_getter) {
272 function_name2_ = Field::NameFromGetter(function_name_);
273 field_ = cls_.LookupFieldAllowPrivate(function_name2_);
274 }
275 if (field_.IsNull() && is_init) {
276 function_name2_ = Field::NameFromInit(function_name_);
277 field_ = cls_.LookupFieldAllowPrivate(function_name2_);
278 }
279 }
280
281 if (!field_.IsNull() && field_.is_const() && field_.is_static() &&
282 (field_.StaticValue() == Object::sentinel().raw())) {
283 processed = true;
284 error_ = field_.InitializeStatic();
285 if (error_.IsError()) {
286 if (FLAG_trace_compilation_trace) {
287 THR_Print(
288 "Compilation trace: error initializing field %s for %s,%s,%s "
289 "(%s)\n",
290 field_.ToCString(), uri_.ToCString(), class_name_.ToCString(),
291 function_name_.ToCString(), Error::Cast(error_).ToErrorCString());
292 }
293 return error_.raw();
294 }
295 }
296
297 if (!function_.IsNull()) {
298 processed = true;
299 error_ = CompileFunction(function_);
300 if (error_.IsError()) {
301 if (FLAG_trace_compilation_trace) {
302 THR_Print("Compilation trace: error compiling %s,%s,%s (%s)\n",
303 uri_.ToCString(), class_name_.ToCString(),
304 function_name_.ToCString(),
305 Error::Cast(error_).ToErrorCString());
306 }
307 return error_.raw();
308 }
309 if (add_closure) {
310 function_ = function_.ImplicitClosureFunction();
311 error_ = CompileFunction(function_);
312 if (error_.IsError()) {
313 if (FLAG_trace_compilation_trace) {
314 THR_Print(
315 "Compilation trace: error compiling closure %s,%s,%s (%s)\n",
316 uri_.ToCString(), class_name_.ToCString(),
317 function_name_.ToCString(), Error::Cast(error_).ToErrorCString());
318 }
319 return error_.raw();
320 }
321 } else if (is_dyn) {
322 function_name_ = function_.name(); // With private mangling.
323 function_name_ =
324 Function::CreateDynamicInvocationForwarderName(function_name_);
325 function_ = function_.GetDynamicInvocationForwarder(function_name_);
326 error_ = CompileFunction(function_);
327 if (error_.IsError()) {
328 if (FLAG_trace_compilation_trace) {
329 THR_Print(
330 "Compilation trace: error compiling dynamic forwarder %s,%s,%s "
331 "(%s)\n",
332 uri_.ToCString(), class_name_.ToCString(),
333 function_name_.ToCString(), Error::Cast(error_).ToErrorCString());
334 }
335 return error_.raw();
336 }
337 }
338 }
339
340 if (!field_.IsNull() && field_.is_static() && !field_.is_const() &&
341 field_.has_nontrivial_initializer()) {
342 processed = true;
343 function_ = field_.EnsureInitializerFunction();
344 error_ = CompileFunction(function_);
345 if (error_.IsError()) {
346 if (FLAG_trace_compilation_trace) {
347 THR_Print(
348 "Compilation trace: error compiling initializer %s,%s,%s (%s)\n",
349 uri_.ToCString(), class_name_.ToCString(),
350 function_name_.ToCString(), Error::Cast(error_).ToErrorCString());
351 }
352 return error_.raw();
353 }
354 }
355
356 if (FLAG_trace_compilation_trace) {
357 if (!processed) {
358 THR_Print("Compilation trace: ignored %s,%s,%s\n", uri_.ToCString(),
359 class_name_.ToCString(), function_name_.ToCString());
360 }
361 }
362 return Object::null();
363}
364
365ObjectPtr CompilationTraceLoader::CompileFunction(const Function& function) {
366 if (function.is_abstract() || function.HasCode()) {
367 return Object::null();
368 }
369
370 error_ = Compiler::CompileFunction(thread_, function);
371 if (error_.IsError()) {
372 return error_.raw();
373 }
374
375 SpeculateInstanceCallTargets(function);
376
377 return error_.raw();
378}
379
380// For instance calls, if the receiver's static type has one concrete
381// implementation, lookup the target for that implementation and add it
382// to the ICData's entries.
383// For some well-known interfaces, do the same for the most common concrete
384// implementation (e.g., int -> _Smi).
385void CompilationTraceLoader::SpeculateInstanceCallTargets(
386 const Function& function) {
387 sites_ = function.ic_data_array();
388 if (sites_.IsNull()) {
389 return;
390 }
391 for (intptr_t i = 1; i < sites_.Length(); i++) {
392 site_ ^= sites_.At(i);
393 if (site_.rebind_rule() != ICData::kInstance) {
394 continue;
395 }
396 if (site_.NumArgsTested() != 1) {
397 continue;
398 }
399
400 static_type_ = site_.receivers_static_type();
401 if (static_type_.IsNull()) {
402 continue;
403 } else if (static_type_.IsDoubleType()) {
404 receiver_cls_ = Isolate::Current()->class_table()->At(kDoubleCid);
405 } else if (static_type_.IsIntType()) {
406 receiver_cls_ = Isolate::Current()->class_table()->At(kSmiCid);
407 } else if (static_type_.IsStringType()) {
408 receiver_cls_ = Isolate::Current()->class_table()->At(kOneByteStringCid);
409 } else if (static_type_.IsDartFunctionType() ||
410 static_type_.IsDartClosureType()) {
411 receiver_cls_ = Isolate::Current()->class_table()->At(kClosureCid);
412 } else if (static_type_.HasTypeClass()) {
413 receiver_cls_ = static_type_.type_class();
414 if (receiver_cls_.is_implemented() || receiver_cls_.is_abstract()) {
415 continue;
416 }
417 } else {
418 continue;
419 }
420
421 selector_ = site_.target_name();
422 args_desc_ = site_.arguments_descriptor();
423 target_ = Resolver::ResolveDynamicForReceiverClass(
424 receiver_cls_, selector_, ArgumentsDescriptor(args_desc_));
425 if (!target_.IsNull() && !site_.HasReceiverClassId(receiver_cls_.id())) {
426 intptr_t count = 0; // Don't pollute type feedback and coverage data.
427 site_.AddReceiverCheck(receiver_cls_.id(), target_, count);
428 }
429 }
430}
431
432TypeFeedbackSaver::TypeFeedbackSaver(WriteStream* stream)
433 : stream_(stream),
434 cls_(Class::Handle()),
435 lib_(Library::Handle()),
436 str_(String::Handle()),
437 fields_(Array::Handle()),
438 field_(Field::Handle()),
439 code_(Code::Handle()),
440 call_sites_(Array::Handle()),
441 call_site_(ICData::Handle()) {}
442
443// These flags affect deopt ids.
444static char* CompilerFlags() {
445 TextBuffer buffer(64);
446
447#define ADD_FLAG(flag) buffer.AddString(FLAG_##flag ? " " #flag : " no-" #flag)
448 ADD_FLAG(enable_asserts);
449 ADD_FLAG(use_field_guards);
450 ADD_FLAG(use_osr);
451 ADD_FLAG(causal_async_stacks);
452 ADD_FLAG(fields_may_be_reset);
453#undef ADD_FLAG
454
455 return buffer.Steal();
456}
457
458void TypeFeedbackSaver::WriteHeader() {
459 const char* expected_version = Version::SnapshotString();
460 ASSERT(expected_version != NULL);
461 const intptr_t version_len = strlen(expected_version);
462 stream_->WriteBytes(reinterpret_cast<const uint8_t*>(expected_version),
463 version_len);
464
465 char* expected_features = CompilerFlags();
466 ASSERT(expected_features != NULL);
467 const intptr_t features_len = strlen(expected_features);
468 stream_->WriteBytes(reinterpret_cast<const uint8_t*>(expected_features),
469 features_len + 1);
470 free(expected_features);
471}
472
473void TypeFeedbackSaver::SaveClasses() {
474 ClassTable* table = Isolate::Current()->class_table();
475
476 intptr_t num_cids = table->NumCids();
477 WriteInt(num_cids);
478
479 for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
480 cls_ = table->At(cid);
481 WriteClassByName(cls_);
482 }
483}
484
485void TypeFeedbackSaver::SaveFields() {
486 ClassTable* table = Isolate::Current()->class_table();
487 intptr_t num_cids = table->NumCids();
488 for (intptr_t cid = kNumPredefinedCids; cid < num_cids; cid++) {
489 cls_ = table->At(cid);
490 WriteClassByName(cls_);
491
492 fields_ = cls_.fields();
493 WriteInt(fields_.Length());
494 for (intptr_t i = 0; i < fields_.Length(); i++) {
495 field_ ^= fields_.At(i);
496
497 str_ = field_.name();
498 str_ = String::RemovePrivateKey(str_);
499 WriteString(str_);
500
501 WriteInt(field_.guarded_cid());
502 WriteInt(static_cast<intptr_t>(field_.is_nullable()));
503 }
504 }
505}
506
507void TypeFeedbackSaver::VisitFunction(const Function& function) {
508 if (!function.HasCode()) {
509 return; // Not compiled.
510 }
511
512 cls_ = function.Owner();
513 WriteClassByName(cls_);
514
515 str_ = function.name();
516 str_ = String::RemovePrivateKey(str_);
517 WriteString(str_);
518
519 WriteInt(function.kind());
520 WriteInt(function.token_pos().value());
521
522 code_ = function.CurrentCode();
523 intptr_t usage = function.usage_counter();
524 if (usage < 0) {
525 // Usage is set to INT32_MIN while in the background compilation queue ...
526 usage = (usage - INT32_MIN) + FLAG_optimization_counter_threshold;
527 } else if (code_.is_optimized()) {
528 // ... and set to 0 when an optimizing compile completes.
529 usage = usage + FLAG_optimization_counter_threshold;
530 }
531 WriteInt(usage);
532
533 WriteInt(function.inlining_depth());
534
535 call_sites_ = function.ic_data_array();
536 if (call_sites_.IsNull()) {
537 call_sites_ = Object::empty_array().raw(); // Remove edge case.
538 }
539
540 // First element is edge counters.
541 WriteInt(call_sites_.Length() - 1);
542 for (intptr_t i = 1; i < call_sites_.Length(); i++) {
543 call_site_ ^= call_sites_.At(i);
544
545 WriteInt(call_site_.deopt_id());
546 WriteInt(call_site_.rebind_rule());
547
548 str_ = call_site_.target_name();
549 str_ = String::RemovePrivateKey(str_);
550 WriteString(str_);
551
552 intptr_t num_checked_arguments = call_site_.NumArgsTested();
553 WriteInt(num_checked_arguments);
554
555 intptr_t num_entries = call_site_.NumberOfChecks();
556 WriteInt(num_entries);
557
558 for (intptr_t entry_index = 0; entry_index < num_entries; entry_index++) {
559 WriteInt(call_site_.GetCountAt(entry_index));
560
561 for (intptr_t argument_index = 0; argument_index < num_checked_arguments;
562 argument_index++) {
563 WriteInt(call_site_.GetClassIdAt(entry_index, argument_index));
564 }
565 }
566 }
567}
568
569void TypeFeedbackSaver::WriteClassByName(const Class& cls) {
570 lib_ = cls.library();
571
572 str_ = lib_.url();
573 WriteString(str_);
574
575 str_ = cls_.Name();
576 str_ = String::RemovePrivateKey(str_);
577 WriteString(str_);
578}
579
580void TypeFeedbackSaver::WriteString(const String& value) {
581 const char* cstr = value.ToCString();
582 intptr_t len = strlen(cstr);
583 stream_->WriteUnsigned(len);
584 stream_->WriteBytes(cstr, len);
585}
586
587TypeFeedbackLoader::TypeFeedbackLoader(Thread* thread)
588 : thread_(thread),
589 zone_(thread->zone()),
590 stream_(nullptr),
591 cid_map_(nullptr),
592 uri_(String::Handle(zone_)),
593 lib_(Library::Handle(zone_)),
594 cls_name_(String::Handle(zone_)),
595 cls_(Class::Handle(zone_)),
596 field_name_(String::Handle(zone_)),
597 fields_(Array::Handle(zone_)),
598 field_(Field::Handle(zone_)),
599 func_name_(String::Handle(zone_)),
600 func_(Function::Handle(zone_)),
601 call_sites_(Array::Handle(zone_)),
602 call_site_(ICData::Handle(zone_)),
603 target_name_(String::Handle(zone_)),
604 target_(Function::Handle(zone_)),
605 args_desc_(Array::Handle(zone_)),
606 functions_to_compile_(
607 GrowableObjectArray::Handle(zone_, GrowableObjectArray::New())),
608 error_(Error::Handle(zone_)) {}
609
610TypeFeedbackLoader::~TypeFeedbackLoader() {
611 delete[] cid_map_;
612}
613
614ObjectPtr TypeFeedbackLoader::LoadFeedback(ReadStream* stream) {
615 stream_ = stream;
616
617 error_ = CheckHeader();
618 if (error_.IsError()) {
619 return error_.raw();
620 }
621
622 error_ = LoadClasses();
623 if (error_.IsError()) {
624 return error_.raw();
625 }
626
627 error_ = LoadFields();
628 if (error_.IsError()) {
629 return error_.raw();
630 }
631
632 while (stream_->PendingBytes() > 0) {
633 error_ = LoadFunction();
634 if (error_.IsError()) {
635 return error_.raw();
636 }
637 }
638
639 while (functions_to_compile_.Length() > 0) {
640 func_ ^= functions_to_compile_.RemoveLast();
641
642 if (Compiler::CanOptimizeFunction(thread_, func_) &&
643 (func_.usage_counter() >= FLAG_optimization_counter_threshold)) {
644 error_ = Compiler::CompileOptimizedFunction(thread_, func_);
645 if (error_.IsError()) {
646 return error_.raw();
647 }
648 }
649 }
650
651 if (FLAG_trace_compilation_trace) {
652 THR_Print("Done loading feedback\n");
653 }
654
655 return Error::null();
656}
657
658ObjectPtr TypeFeedbackLoader::CheckHeader() {
659 const char* expected_version = Version::SnapshotString();
660 ASSERT(expected_version != NULL);
661 const intptr_t version_len = strlen(expected_version);
662 if (stream_->PendingBytes() < version_len) {
663 const intptr_t kMessageBufferSize = 128;
664 char message_buffer[kMessageBufferSize];
665 Utils::SNPrint(message_buffer, kMessageBufferSize,
666 "No snapshot version found, expected '%s'",
667 expected_version);
668 const String& msg = String::Handle(String::New(message_buffer, Heap::kOld));
669 return ApiError::New(msg, Heap::kOld);
670 }
671
672 const char* version =
673 reinterpret_cast<const char*>(stream_->AddressOfCurrentPosition());
674 ASSERT(version != NULL);
675 if (strncmp(version, expected_version, version_len) != 0) {
676 const intptr_t kMessageBufferSize = 256;
677 char message_buffer[kMessageBufferSize];
678 char* actual_version = Utils::StrNDup(version, version_len);
679 Utils::SNPrint(message_buffer, kMessageBufferSize,
680 "Wrong snapshot version, expected '%s' found '%s'",
681 expected_version, actual_version);
682 free(actual_version);
683 const String& msg = String::Handle(String::New(message_buffer, Heap::kOld));
684 return ApiError::New(msg, Heap::kOld);
685 }
686 stream_->Advance(version_len);
687
688 char* expected_features = CompilerFlags();
689 ASSERT(expected_features != NULL);
690 const intptr_t expected_len = strlen(expected_features);
691
692 const char* features =
693 reinterpret_cast<const char*>(stream_->AddressOfCurrentPosition());
694 ASSERT(features != NULL);
695 intptr_t buffer_len = Utils::StrNLen(features, stream_->PendingBytes());
696 if ((buffer_len != expected_len) ||
697 (strncmp(features, expected_features, expected_len) != 0)) {
698 const String& msg = String::Handle(String::NewFormatted(
699 Heap::kOld,
700 "Feedback not compatible with the current VM configuration: "
701 "the feedback requires '%.*s' but the VM has '%s'",
702 static_cast<int>(buffer_len > 1024 ? 1024 : buffer_len), features,
703 expected_features));
704 free(expected_features);
705 return ApiError::New(msg, Heap::kOld);
706 }
707 free(expected_features);
708 stream_->Advance(expected_len + 1);
709 return Error::null();
710}
711
712ObjectPtr TypeFeedbackLoader::LoadClasses() {
713 num_cids_ = ReadInt();
714
715 cid_map_ = new intptr_t[num_cids_];
716 for (intptr_t cid = 0; cid < num_cids_; cid++) {
717 cid_map_[cid] = kIllegalCid;
718 }
719 for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) {
720 cid_map_[cid] = cid;
721 }
722
723 for (intptr_t cid = kNumPredefinedCids; cid < num_cids_; cid++) {
724 cls_ = ReadClassByName();
725 if (!cls_.IsNull()) {
726 cid_map_[cid] = cls_.id();
727 }
728 }
729
730 return Error::null();
731}
732
733ObjectPtr TypeFeedbackLoader::LoadFields() {
734 for (intptr_t cid = kNumPredefinedCids; cid < num_cids_; cid++) {
735 cls_ = ReadClassByName();
736 bool skip = cls_.IsNull();
737
738 intptr_t num_fields = ReadInt();
739 if (!skip && (num_fields > 0)) {
740 error_ = cls_.EnsureIsFinalized(thread_);
741 if (error_.IsError()) {
742 return error_.raw();
743 }
744 fields_ = cls_.fields();
745 }
746
747 for (intptr_t i = 0; i < num_fields; i++) {
748 field_name_ = ReadString();
749 intptr_t guarded_cid = cid_map_[ReadInt()];
750 intptr_t is_nullable = ReadInt();
751
752 if (skip) {
753 continue;
754 }
755
756 if (i >= fields_.Length()) {
757 if (FLAG_trace_compilation_trace) {
758 THR_Print("Missing field %s\n", field_name_.ToCString());
759 }
760 continue;
761 }
762
763 field_ ^= fields_.At(i);
764 if (!String::EqualsIgnoringPrivateKey(String::Handle(field_.name()),
765 field_name_)) {
766 if (FLAG_trace_compilation_trace) {
767 THR_Print("Missing field %s\n", field_name_.ToCString());
768 }
769 continue;
770 }
771
772 if (guarded_cid == kIllegalCid) {
773 // Guarded CID from feedback is not in current program: assume the field
774 // will become polymorphic.
775 field_.set_guarded_cid(kDynamicCid);
776 field_.set_is_nullable(true);
777 } else if ((field_.guarded_cid() != kIllegalCid) &&
778 (field_.guarded_cid() == guarded_cid)) {
779 // Guarded CID from feedback is different from initialized guarded CID
780 // in the current program: assume the field will become polymorphic.
781 field_.set_guarded_cid(kDynamicCid);
782 field_.set_is_nullable(true);
783 } else {
784 field_.set_guarded_cid(guarded_cid);
785 field_.set_is_nullable((is_nullable != 0) || field_.is_nullable());
786 }
787
788 // TODO(rmacnak): Merge other field type feedback.
789 field_.set_guarded_list_length(Field::kNoFixedLength);
790 field_.set_guarded_list_length_in_object_offset(
791 Field::kUnknownLengthOffset);
792 field_.set_static_type_exactness_state(
793 StaticTypeExactnessState::NotTracking());
794 field_.DeoptimizeDependentCode();
795 }
796 }
797
798 return Error::null();
799}
800
801ObjectPtr TypeFeedbackLoader::LoadFunction() {
802 bool skip = false;
803
804 cls_ = ReadClassByName();
805 if (!cls_.IsNull()) {
806 error_ = cls_.EnsureIsFinalized(thread_);
807 if (error_.IsError()) {
808 return error_.raw();
809 }
810 } else {
811 skip = true;
812 }
813
814 func_name_ = ReadString(); // Without private mangling.
815 FunctionLayout::Kind kind = static_cast<FunctionLayout::Kind>(ReadInt());
816 intptr_t token_pos = ReadInt();
817 intptr_t usage = ReadInt();
818 intptr_t inlining_depth = ReadInt();
819 intptr_t num_call_sites = ReadInt();
820
821 if (!skip) {
822 func_ = FindFunction(kind, token_pos);
823 if (func_.IsNull()) {
824 skip = true;
825 if (FLAG_trace_compilation_trace) {
826 THR_Print("Missing function %s %s\n", func_name_.ToCString(),
827 Function::KindToCString(kind));
828 }
829 }
830 }
831
832 if (!skip) {
833 error_ = Compiler::CompileFunction(thread_, func_);
834 if (error_.IsError()) {
835 return error_.raw();
836 }
837 call_sites_ = func_.ic_data_array();
838 if (call_sites_.IsNull()) {
839 call_sites_ = Object::empty_array().raw(); // Remove edge case.
840 }
841 if (call_sites_.Length() != num_call_sites + 1) {
842 skip = true;
843 if (FLAG_trace_compilation_trace) {
844 THR_Print("Mismatched call site count %s %" Pd " %" Pd "\n",
845 func_name_.ToCString(), call_sites_.Length(), num_call_sites);
846 }
847 }
848 }
849
850 // First element is edge counters.
851 for (intptr_t i = 1; i <= num_call_sites; i++) {
852 intptr_t deopt_id = ReadInt();
853 intptr_t rebind_rule = ReadInt();
854 target_name_ = ReadString();
855 intptr_t num_checked_arguments = ReadInt();
856 intptr_t num_entries = ReadInt();
857
858 if (!skip) {
859 call_site_ ^= call_sites_.At(i);
860 if ((call_site_.deopt_id() != deopt_id) ||
861 (call_site_.rebind_rule() != rebind_rule) ||
862 (call_site_.NumArgsTested() != num_checked_arguments)) {
863 skip = true;
864 if (FLAG_trace_compilation_trace) {
865 THR_Print("Mismatched call site %s\n", call_site_.ToCString());
866 }
867 }
868 }
869
870 for (intptr_t entry_index = 0; entry_index < num_entries; entry_index++) {
871 intptr_t entry_usage = ReadInt();
872 bool skip_entry = skip;
873 GrowableArray<intptr_t> cids(num_checked_arguments);
874
875 for (intptr_t argument_index = 0; argument_index < num_checked_arguments;
876 argument_index++) {
877 intptr_t cid = cid_map_[ReadInt()];
878 cids.Add(cid);
879 if (cid == kIllegalCid) {
880 // Alternative: switch to a sentinel value such as kDynamicCid and
881 // have the optimizer generate a megamorphic call.
882 skip_entry = true;
883 }
884 }
885
886 if (skip_entry) {
887 continue;
888 }
889
890 intptr_t reuse_index = call_site_.FindCheck(cids);
891 if (reuse_index == -1) {
892 cls_ = thread_->isolate()->class_table()->At(cids[0]);
893 // Use target name and args descriptor from the current program
894 // instead of saved feedback to get the correct private mangling and
895 // ensure no arity mismatch crashes.
896 target_name_ = call_site_.target_name();
897 args_desc_ = call_site_.arguments_descriptor();
898 target_ = Resolver::ResolveDynamicForReceiverClass(
899 cls_, target_name_, ArgumentsDescriptor(args_desc_));
900 if (!target_.IsNull()) {
901 if (num_checked_arguments == 1) {
902 call_site_.AddReceiverCheck(cids[0], target_, entry_usage);
903 } else {
904 call_site_.AddCheck(cids, target_, entry_usage);
905 }
906 }
907 } else {
908 call_site_.IncrementCountAt(reuse_index, entry_usage);
909 }
910 }
911 }
912
913 if (!skip) {
914 func_.set_usage_counter(usage);
915 func_.set_inlining_depth(inlining_depth);
916
917 // Delay compilation until all feedback is loaded so feedback is available
918 // for inlined functions.
919 functions_to_compile_.Add(func_);
920 }
921
922 return Error::null();
923}
924
925FunctionPtr TypeFeedbackLoader::FindFunction(FunctionLayout::Kind kind,
926 intptr_t token_pos) {
927 if (cls_name_.Equals(Symbols::TopLevel())) {
928 func_ = lib_.LookupFunctionAllowPrivate(func_name_);
929 } else {
930 func_ = cls_.LookupFunctionAllowPrivate(func_name_);
931 }
932
933 if (!func_.IsNull()) {
934 // Found regular method.
935 } else if (kind == FunctionLayout::kMethodExtractor) {
936 ASSERT(Field::IsGetterName(func_name_));
937 // Without private mangling:
938 String& name = String::Handle(zone_, Field::NameFromGetter(func_name_));
939 func_ = cls_.LookupFunctionAllowPrivate(name);
940 if (!func_.IsNull() && func_.IsDynamicFunction()) {
941 name = func_.name(); // With private mangling.
942 name = Field::GetterName(name);
943 func_ = func_.GetMethodExtractor(name);
944 } else {
945 func_ = Function::null();
946 }
947 } else if (kind == FunctionLayout::kDynamicInvocationForwarder) {
948 // Without private mangling:
949 String& name = String::Handle(
950 zone_, Function::DemangleDynamicInvocationForwarderName(func_name_));
951 func_ = cls_.LookupFunctionAllowPrivate(name);
952 if (!func_.IsNull() && func_.IsDynamicFunction()) {
953 name = func_.name(); // With private mangling.
954 name = Function::CreateDynamicInvocationForwarderName(name);
955 func_ = func_.CreateDynamicInvocationForwarder(name);
956 } else {
957 func_ = Function::null();
958 }
959 } else if (kind == FunctionLayout::kClosureFunction) {
960 // Note this lookup relies on parent functions appearing before child
961 // functions in the serialized feedback, so the parent will have already
962 // been unoptimized compilated and the child function created and added to
963 // ObjectStore::closure_functions_.
964 const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle(
965 zone_, thread_->isolate()->object_store()->closure_functions());
966 bool found = false;
967 for (intptr_t i = 0; i < closure_functions.Length(); i++) {
968 func_ ^= closure_functions.At(i);
969 if ((func_.Owner() == cls_.raw()) &&
970 (func_.token_pos().value() == token_pos)) {
971 found = true;
972 break;
973 }
974 }
975 if (!found) {
976 func_ = Function::null();
977 }
978 } else {
979 // This leaves unhandled:
980 // - kInvokeFieldDispatcher (how to get a valid args descriptor?)
981 // - static field getters
982 // - static field initializers (not retained by the field object)
983 }
984
985 if (!func_.IsNull()) {
986 if (kind == FunctionLayout::kImplicitClosureFunction) {
987 func_ = func_.ImplicitClosureFunction();
988 }
989 if (func_.is_abstract() || (func_.kind() != kind)) {
990 func_ = Function::null();
991 }
992 }
993
994 return func_.raw();
995}
996
997ClassPtr TypeFeedbackLoader::ReadClassByName() {
998 uri_ = ReadString();
999 cls_name_ = ReadString();
1000
1001 lib_ = Library::LookupLibrary(thread_, uri_);
1002 if (lib_.IsNull()) {
1003 if (FLAG_trace_compilation_trace) {
1004 THR_Print("Missing library %s\n", uri_.ToCString());
1005 }
1006 return Class::null();
1007 }
1008
1009 if (cls_name_.Equals(Symbols::TopLevel())) {
1010 cls_ = lib_.toplevel_class();
1011 } else {
1012 cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(cls_name_);
1013 if (cls_.IsNull()) {
1014 if (FLAG_trace_compilation_trace) {
1015 THR_Print("Missing class %s %s\n", uri_.ToCString(),
1016 cls_name_.ToCString());
1017 }
1018 }
1019 }
1020 return cls_.raw();
1021}
1022
1023StringPtr TypeFeedbackLoader::ReadString() {
1024 intptr_t len = stream_->ReadUnsigned();
1025 const char* cstr =
1026 reinterpret_cast<const char*>(stream_->AddressOfCurrentPosition());
1027 stream_->Advance(len);
1028 return Symbols::New(thread_, cstr, len);
1029}
1030
1031#endif // !defined(DART_PRECOMPILED_RUNTIME)
1032
1033} // namespace dart
1034