Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: runtime/vm/isolate_reload.cc

Issue 1965823002: Initial isolate reload support (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016, 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/isolate_reload.h"
6
7 #include "vm/become.h"
8 #include "vm/code_generator.h"
9 #include "vm/compiler.h"
10 #include "vm/dart_api_impl.h"
11 #include "vm/hash_table.h"
12 #include "vm/isolate.h"
13 #include "vm/log.h"
14 #include "vm/object.h"
15 #include "vm/object_store.h"
16 #include "vm/parser.h"
17 #include "vm/safepoint.h"
18 #include "vm/service_event.h"
19 #include "vm/stack_frame.h"
20 #include "vm/thread.h"
21 #include "vm/timeline.h"
22 #include "vm/visitor.h"
23
24 namespace dart {
25
26 DEFINE_FLAG(bool, trace_reload, false, "Trace isolate reloading");
27 DEFINE_FLAG(bool, identity_reload, false, "Enable checks for identity reload.");
28 DEFINE_FLAG(int, reload_every, 0, "Reload every N stack overflow checks.");
29 DEFINE_FLAG(bool, reload_every_optimized, true, "Only from optimized code.");
30
31 #ifndef PRODUCT
32
33 #define I (isolate())
34 #define Z (thread->zone())
35
36 #define TIMELINE_SCOPE(name) \
37 TimelineDurationScope tds##name(Thread::Current(), \
38 Timeline::GetIsolateStream(), \
39 #name)
40
41
42 class ScriptUrlSetTraits {
43 public:
44 static bool ReportStats() { return false; }
45 static const char* Name() { return "ScriptUrlSetTraits"; }
46
47 static bool IsMatch(const Object& a, const Object& b) {
48 if (!a.IsString() || !b.IsString()) {
49 return false;
50 }
51
52 return String::Cast(a).Equals(String::Cast(b));
53 }
54
55 static uword Hash(const Object& obj) {
56 return String::Cast(obj).Hash();
57 }
58 };
59
60
61 class ClassMapTraits {
62 public:
63 static bool ReportStats() { return false; }
64 static const char* Name() { return "ClassMapTraits"; }
65
66 static bool IsMatch(const Object& a, const Object& b) {
67 if (!a.IsClass() || !b.IsClass()) {
68 return false;
69 }
70 return IsolateReloadContext::IsSameClass(Class::Cast(a), Class::Cast(b));
71 }
72
73 static uword Hash(const Object& obj) {
74 return String::HashRawSymbol(Class::Cast(obj).Name());
75 }
76 };
77
78
79 class LibraryMapTraits {
80 public:
81 static bool ReportStats() { return false; }
82 static const char* Name() { return "LibraryMapTraits"; }
83
84 static bool IsMatch(const Object& a, const Object& b) {
85 if (!a.IsLibrary() || !b.IsLibrary()) {
86 return false;
87 }
88 return IsolateReloadContext::IsSameLibrary(
89 Library::Cast(a), Library::Cast(b));
90 }
91
92 static uword Hash(const Object& obj) {
93 return Library::Cast(obj).UrlHash();
94 }
95 };
96
97
98 class BecomeMapTraits {
99 public:
100 static bool ReportStats() { return false; }
101 static const char* Name() { return "BecomeMapTraits"; }
102
103 static bool IsMatch(const Object& a, const Object& b) {
104 return a.raw() == b.raw();
105 }
106
107 static uword Hash(const Object& obj) {
108 if (obj.IsLibrary()) {
109 return Library::Cast(obj).UrlHash();
110 } else if (obj.IsClass()) {
111 if (Class::Cast(obj).id() == kFreeListElement) {
112 return 0;
113 }
114 return String::HashRawSymbol(Class::Cast(obj).Name());
115 } else if (obj.IsField()) {
116 return String::HashRawSymbol(Field::Cast(obj).name());
117 }
118 return 0;
119 }
120 };
121
122
123 bool IsolateReloadContext::IsSameField(const Field& a, const Field& b) {
124 if (a.is_static() != b.is_static()) {
125 return false;
126 }
127 const Class& a_cls = Class::Handle(a.Owner());
128 const Class& b_cls = Class::Handle(b.Owner());
129
130 if (!IsSameClass(a_cls, b_cls)) {
131 return false;
132 }
133
134 const String& a_name = String::Handle(a.name());
135 const String& b_name = String::Handle(b.name());
136
137 return a_name.Equals(b_name);
138 }
139
140
141 bool IsolateReloadContext::IsSameClass(const Class& a, const Class& b) {
142 if (a.is_patch() != b.is_patch()) {
143 // TODO(johnmccutchan): Should we just check the class kind bits?
144 return false;
145 }
146
147 // TODO(turnidge): We need to look at generic type arguments for
148 // synthetic mixin classes. Their names are not necessarily unique
149 // currently.
150 const String& a_name = String::Handle(Class::Cast(a).Name());
151 const String& b_name = String::Handle(Class::Cast(b).Name());
152
153 if (!a_name.Equals(b_name)) {
154 return false;
155 }
156
157 const Library& a_lib = Library::Handle(Class::Cast(a).library());
158 const Library& b_lib = Library::Handle(Class::Cast(b).library());
159 return IsSameLibrary(a_lib, b_lib);
160 }
161
162
163 bool IsolateReloadContext::IsSameLibrary(
164 const Library& a_lib, const Library& b_lib) {
165 const String& a_lib_url =
166 String::Handle(a_lib.IsNull() ? String::null() : a_lib.url());
167 const String& b_lib_url =
168 String::Handle(b_lib.IsNull() ? String::null() : b_lib.url());
169 return a_lib_url.Equals(b_lib_url);
170 }
171
172
173 IsolateReloadContext::IsolateReloadContext(Isolate* isolate, bool test_mode)
174 : start_time_micros_(OS::GetCurrentMonotonicMicros()),
175 isolate_(isolate),
176 test_mode_(test_mode),
177 has_error_(false),
178 saved_num_cids_(-1),
179 saved_class_table_(NULL),
180 num_saved_libs_(-1),
181 script_uri_(String::null()),
182 error_(Error::null()),
183 clean_scripts_set_storage_(Array::null()),
184 compile_time_constants_(Array::null()),
185 old_classes_set_storage_(Array::null()),
186 class_map_storage_(Array::null()),
187 old_libraries_set_storage_(Array::null()),
188 library_map_storage_(Array::null()),
189 become_map_storage_(Array::null()),
190 saved_root_library_(Library::null()),
191 saved_libraries_(GrowableObjectArray::null()) {
192 // Preallocate storage for maps.
193 clean_scripts_set_storage_ =
194 HashTables::New<UnorderedHashSet<ScriptUrlSetTraits> >(4);
195 old_classes_set_storage_ =
196 HashTables::New<UnorderedHashSet<ClassMapTraits> >(4);
197 class_map_storage_ =
198 HashTables::New<UnorderedHashMap<ClassMapTraits> >(4);
199 old_libraries_set_storage_ =
200 HashTables::New<UnorderedHashSet<LibraryMapTraits> >(4);
201 library_map_storage_ =
202 HashTables::New<UnorderedHashMap<LibraryMapTraits> >(4);
203 become_map_storage_ =
204 HashTables::New<UnorderedHashMap<BecomeMapTraits> >(4);
205 }
206
207
208 IsolateReloadContext::~IsolateReloadContext() {
209 }
210
211
212 void IsolateReloadContext::ReportError(const Error& error) {
213 has_error_ = true;
214 error_ = error.raw();
215 if (FLAG_trace_reload) {
216 THR_Print("ISO-RELOAD: Error: %s\n", error.ToErrorCString());
217 }
218 ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload);
219 service_event.set_reload_error(&error);
220 Service::HandleEvent(&service_event);
221 }
222
223
224 void IsolateReloadContext::ReportError(const String& error_msg) {
225 ReportError(LanguageError::Handle(LanguageError::New(error_msg)));
226 }
227
228
229 void IsolateReloadContext::ReportSuccess() {
230 ServiceEvent service_event(Isolate::Current(), ServiceEvent::kIsolateReload);
231 Service::HandleEvent(&service_event);
232 }
233
234
235 void IsolateReloadContext::StartReload() {
236 Thread* thread = Thread::Current();
237
238 // Grab root library before calling CheckpointBeforeReload.
239 const Library& root_lib = Library::Handle(object_store()->root_library());
240 ASSERT(!root_lib.IsNull());
241 const String& root_lib_url = String::Handle(root_lib.url());
242
243 // Ensure all functions on the stack have unoptimized code.
244 EnsuredUnoptimizedCodeForStack();
245 // Deoptimize all code that had optimizing decisions that are dependent on
246 // assumptions from field guards or CHA or deferred library prefixes.
247 // TODO(johnmccutchan): Deoptimizing dependent code here (before the reload)
248 // is paranoid. This likely can be moved to the commit phase.
249 DeoptimizeDependentCode();
250 Checkpoint();
251
252 // Block class finalization attempts when calling into the library
253 // tag handler.
254 I->BlockClassFinalization();
255 Object& result = Object::Handle(thread->zone());
256 {
257 TransitionVMToNative transition(thread);
258 Api::Scope api_scope(thread);
259
260 Dart_Handle retval =
261 (I->library_tag_handler())(Dart_kScriptTag,
262 Api::NewHandle(thread, Library::null()),
263 Api::NewHandle(thread, root_lib_url.raw()));
264 result = Api::UnwrapHandle(retval);
265 }
266 I->UnblockClassFinalization();
267 if (result.IsError()) {
268 ReportError(Error::Cast(result));
269 }
270 }
271
272
273 void IsolateReloadContext::RegisterClass(const Class& new_cls) {
274 const Class& old_cls = Class::Handle(OldClassOrNull(new_cls));
275 if (old_cls.IsNull()) {
276 Isolate::Current()->class_table()->Register(new_cls);
277
278 if (FLAG_identity_reload) {
279 TIR_Print("Could not find replacement class for %s\n",
280 new_cls.ToCString());
281 UNREACHABLE();
282 }
283
284 // New class maps to itself.
285 AddClassMapping(new_cls, new_cls);
286 return;
287 }
288 new_cls.set_id(old_cls.id());
289 isolate()->class_table()->SetAt(old_cls.id(), new_cls.raw());
290 if (!old_cls.is_enum_class()) {
291 new_cls.CopyCanonicalConstants(old_cls);
292 }
293 new_cls.CopyCanonicalTypes(old_cls);
294 AddBecomeMapping(old_cls, new_cls);
295 AddClassMapping(new_cls, old_cls);
296 }
297
298
299 void IsolateReloadContext::FinishReload() {
300 // Disable the background compiler while we are performing the reload.
301 BackgroundCompiler::Disable();
rmacnak 2016/05/13 17:59:34 This should happen before DeoptimizeDependentCode.
302
303 BuildLibraryMapping();
304 TIR_Print("---- DONE FINALIZING\n");
305 if (ValidateReload()) {
306 Commit();
307 PostCommit();
308 } else {
309 Rollback();
310 }
311 // ValidateReload mutates the direct subclass information and does
312 // not remove dead subclasses. Rebuild the direct subclass
313 // information from scratch.
314 RebuildDirectSubclasses();
315
316 BackgroundCompiler::Enable();
317 }
318
319
320 void IsolateReloadContext::AbortReload(const Error& error) {
321 ReportError(error);
322 Rollback();
323 }
324
325
326 void IsolateReloadContext::EnsuredUnoptimizedCodeForStack() {
327 TIMELINE_SCOPE(EnsuredUnoptimizedCodeForStack);
328 StackFrameIterator it(StackFrameIterator::kDontValidateFrames);
329
330 Function& func = Function::Handle();
331 while (it.HasNextFrame()) {
332 StackFrame* frame = it.NextFrame();
333 if (frame->IsDartFrame()) {
334 func = frame->LookupDartFunction();
335 ASSERT(!func.IsNull());
336 func.EnsureHasCompiledUnoptimizedCode();
337 }
338 }
339 }
340
341
342 void IsolateReloadContext::DeoptimizeDependentCode() {
343 ClassTable* class_table = I->class_table();
344
345 const intptr_t bottom = Dart::vm_isolate()->class_table()->NumCids();
346 const intptr_t top = I->class_table()->NumCids();
347 Class& cls = Class::Handle();
348 Array& fields = Array::Handle();
349 Field& field = Field::Handle();
350 for (intptr_t cls_idx = bottom; cls_idx < top; cls_idx++) {
351 if (!class_table->HasValidClassAt(cls_idx)) {
352 // Skip.
353 continue;
354 }
355
356 // Deoptimize CHA code.
357 cls = class_table->At(cls_idx);
358 ASSERT(!cls.IsNull());
359
360 cls.DisableAllCHAOptimizedCode();
361
362 // Deoptimize field guard code.
363 fields = cls.fields();
364 ASSERT(!fields.IsNull());
365 for (intptr_t field_idx = 0; field_idx < fields.Length(); field_idx++) {
366 field = Field::RawCast(fields.At(field_idx));
367 ASSERT(!field.IsNull());
368 field.DeoptimizeDependentCode();
369 }
370 }
371
372 // TODO(johnmccutchan): Also call LibraryPrefix::InvalidateDependentCode.
373 }
374
375
376 void IsolateReloadContext::CheckpointClasses() {
377 TIMELINE_SCOPE(CheckpointClasses);
378 TIR_Print("---- CHECKPOINTING CLASSES\n");
379 // Checkpoint classes before a reload. We need to copy the following:
380 // 1) The size of the class table.
381 // 2) The class table itself.
382 // For efficiency, we build a set of classes before the reload. This set
383 // is used to pair new classes with old classes.
384
385 ClassTable* class_table = I->class_table();
386
387 // Copy the size of the class table.
388 saved_num_cids_ = I->class_table()->NumCids();
389
390 // Copy of the class table.
391 RawClass** local_saved_class_table =
392 reinterpret_cast<RawClass**>(malloc(sizeof(RawClass*) * saved_num_cids_));
393
394 Class& cls = Class::Handle();
395 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
396 for (intptr_t i = 0; i < saved_num_cids_; i++) {
397 if (class_table->IsValidIndex(i) &&
398 class_table->HasValidClassAt(i)) {
399 // Copy the class into the saved class table and add it to the set.
400 local_saved_class_table[i] = class_table->At(i);
401 if (i != kFreeListElement) {
402 cls = class_table->At(i);
403 bool already_present = old_classes_set.Insert(cls);
404 ASSERT(!already_present);
405 }
406 } else {
407 // No class at this index, mark it as NULL.
408 local_saved_class_table[i] = NULL;
409 }
410 }
411 old_classes_set_storage_ = old_classes_set.Release().raw();
412 // Assigning the field must be done after saving the class table.
413 saved_class_table_ = local_saved_class_table;
414 TIR_Print("---- System had %" Pd " classes\n", saved_num_cids_);
415 }
416
417
418 bool IsolateReloadContext::IsCleanLibrary(const Library& lib) {
419 return lib.is_dart_scheme();
420 }
421
422
423 void IsolateReloadContext::CheckpointLibraries() {
424 TIMELINE_SCOPE(CheckpointLibraries);
425
426 // Save the root library in case we abort the reload.
427 const Library& root_lib =
428 Library::Handle(object_store()->root_library());
429 set_saved_root_library(root_lib);
430
431 // Save the old libraries array in case we abort the reload.
432 const GrowableObjectArray& libs =
433 GrowableObjectArray::Handle(object_store()->libraries());
434 set_saved_libraries(libs);
435
436 // Make a filtered copy of the old libraries array. Keep "clean" libraries
437 // that we will use instead of reloading.
438 const GrowableObjectArray& new_libs = GrowableObjectArray::Handle(
439 GrowableObjectArray::New(Heap::kOld));
440 Library& lib = Library::Handle();
441 UnorderedHashSet<LibraryMapTraits>
442 old_libraries_set(old_libraries_set_storage_);
443 num_saved_libs_ = 0;
444 for (intptr_t i = 0; i < libs.Length(); i++) {
445 lib ^= libs.At(i);
446 if (IsCleanLibrary(lib)) {
447 // We are preserving this library across the reload, assign its new index
448 lib.set_index(new_libs.Length());
449 new_libs.Add(lib, Heap::kOld);
450 num_saved_libs_++;
451 } else {
452 // We are going to reload this library. Clear the index.
453 lib.set_index(-1);
454 }
455 // Add old library to old libraries set.
456 bool already_present = old_libraries_set.Insert(lib);
457 ASSERT(!already_present);
458 }
459 old_libraries_set_storage_ = old_libraries_set.Release().raw();
460
461 // Reset the registered libraries to the filtered array.
462 Library::RegisterLibraries(Thread::Current(), new_libs);
463 // Reset the root library to null.
464 object_store()->set_root_library(Library::Handle());
465 }
466
467
468 void IsolateReloadContext::BuildCleanScriptSet() {
469 const GrowableObjectArray& libs =
470 GrowableObjectArray::Handle(object_store()->libraries());
471
472 UnorderedHashSet<ScriptUrlSetTraits>
473 clean_scripts_set(clean_scripts_set_storage_);
474
475 Library& lib = Library::Handle();
476 Array& scripts = Array::Handle();
477 Script& script = Script::Handle();
478 String& script_url = String::Handle();
479 for (intptr_t lib_idx = 0; lib_idx < libs.Length(); lib_idx++) {
480 lib = Library::RawCast(libs.At(lib_idx));
481 ASSERT(!lib.IsNull());
482 ASSERT(IsCleanLibrary(lib));
483 scripts = lib.LoadedScripts();
484 ASSERT(!scripts.IsNull());
485 for (intptr_t script_idx = 0; script_idx < scripts.Length(); script_idx++) {
486 script = Script::RawCast(scripts.At(script_idx));
487 ASSERT(!script.IsNull());
488 script_url = script.url();
489 ASSERT(!script_url.IsNull());
490 bool already_present = clean_scripts_set.Insert(script_url);
491 ASSERT(!already_present);
492 }
493 }
494
495 clean_scripts_set_storage_ = clean_scripts_set.Release().raw();
496 }
497
498
499 void IsolateReloadContext::FilterCompileTimeConstants() {
500 // Save the compile time constants array.
501 compile_time_constants_ = I->object_store()->compile_time_constants();
502 // Clear the compile time constants array. This will be repopulated
503 // in the loop below.
504 I->object_store()->set_compile_time_constants(Array::Handle());
505
506 if (compile_time_constants_ == Array::null()) {
507 // Nothing to do.
508 return;
509 }
510
511 // Iterate over the saved compile time constants map.
512 ConstantsMap old_constants(compile_time_constants_);
513 ConstantsMap::Iterator it(&old_constants);
514
515 Array& key = Array::Handle();
516 String& url = String::Handle();
517 Smi& token_pos = Smi::Handle();
518 Instance& value = Instance::Handle();
519
520 // We filter the compile time constants map so that after it only contains
521 // constants from scripts contained in this set.
522 UnorderedHashSet<ScriptUrlSetTraits>
523 clean_scripts_set(clean_scripts_set_storage_);
524
525 while (it.MoveNext()) {
526 const intptr_t entry = it.Current();
527 ASSERT(entry != -1);
528 key = Array::RawCast(old_constants.GetKey(entry));
529 ASSERT(!key.IsNull());
530 url = String::RawCast(key.At(0));
531 ASSERT(!url.IsNull());
532 if (clean_scripts_set.ContainsKey(url)) {
533 // We've found a cached constant from a clean script, add it to the
534 // compile time constants map again.
535 token_pos = Smi::RawCast(key.At(1));
536 TokenPosition tp(token_pos.Value());
537 // Use ^= because this might be null.
538 value ^= old_constants.GetPayload(entry, 0);
539 Parser::InsertCachedConstantValue(url, tp, value);
540 }
541 }
542
543 old_constants.Release();
544 clean_scripts_set.Release();
545 }
546
547
548 // While reloading everything we do must be reversible so that we can abort
549 // safely if the reload fails. This function stashes things to the side and
550 // prepares the isolate for the reload attempt.
551 void IsolateReloadContext::Checkpoint() {
552 TIMELINE_SCOPE(Checkpoint);
553 CheckpointClasses();
554 CheckpointLibraries();
555 BuildCleanScriptSet();
556 FilterCompileTimeConstants();
557 }
558
559
560 void IsolateReloadContext::RollbackClasses() {
561 TIR_Print("---- ROLLING BACK CLASS TABLE\n");
562 ASSERT(saved_num_cids_ > 0);
563 ASSERT(saved_class_table_ != NULL);
564 ClassTable* class_table = I->class_table();
565 class_table->SetNumCids(saved_num_cids_);
566 // Overwrite classes in class table with the saved classes.
567 for (intptr_t i = 0; i < saved_num_cids_; i++) {
568 if (class_table->IsValidIndex(i)) {
569 class_table->SetAt(i, saved_class_table_[i]);
570 }
571 }
572 free(saved_class_table_);
573 saved_class_table_ = NULL;
574 saved_num_cids_ = 0;
575 }
576
577
578 void IsolateReloadContext::RollbackLibraries() {
579 TIR_Print("---- ROLLING BACK LIBRARY CHANGES\n");
580 Thread* thread = Thread::Current();
581 Library& lib = Library::Handle();
582 GrowableObjectArray& saved_libs = GrowableObjectArray::Handle(
583 Z, saved_libraries());
584 if (!saved_libs.IsNull()) {
585 for (intptr_t i = 0; i < saved_libs.Length(); i++) {
586 lib = Library::RawCast(saved_libs.At(i));
587 // Restore indexes that were modified in CheckpointLibraries.
588 lib.set_index(i);
589 }
590
591 // Reset the registered libraries to the filtered array.
592 Library::RegisterLibraries(Thread::Current(), saved_libs);
593 }
594
595 Library& saved_root_lib = Library::Handle(Z, saved_root_library());
596 if (!saved_root_lib.IsNull()) {
597 object_store()->set_root_library(saved_root_lib);
598 }
599
600 set_saved_root_library(Library::Handle());
601 set_saved_libraries(GrowableObjectArray::Handle());
602 }
603
604
605 void IsolateReloadContext::Rollback() {
606 I->object_store()->set_compile_time_constants(
607 Array::Handle(compile_time_constants_));
608 RollbackClasses();
609 RollbackLibraries();
610 }
611
612
613 #ifdef DEBUG
614 void IsolateReloadContext::VerifyMaps() {
615 Class& cls = Class::Handle();
616 Class& new_cls = Class::Handle();
617 Class& cls2 = Class::Handle();
618 Class& new_cls2 = Class::Handle();
619
620 // Verify that two old classes aren't both mapped to the same new
621 // class. This could happen is the IsSameClass function is broken.
622 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_);
623 {
624 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map);
625 while (it.MoveNext()) {
626 const intptr_t entry = it.Current();
627 new_cls = Class::RawCast(class_map.GetKey(entry));
628 cls = Class::RawCast(class_map.GetPayload(entry, 0));
629 if (new_cls.raw() != cls.raw()) {
630 UnorderedHashMap<ClassMapTraits>::Iterator it2(&class_map);
631 while (it2.MoveNext()) {
632 new_cls2 = Class::RawCast(class_map.GetKey(entry));
633 if (new_cls.raw() == new_cls2.raw()) {
634 cls2 = Class::RawCast(class_map.GetPayload(entry, 0));
635 if (cls.raw() != cls2.raw()) {
636 OS::PrintErr(
637 "Classes '%s' and '%s' are distinct classes but both map to "
638 "class '%s'\n",
639 cls.ToCString(), cls2.ToCString(), new_cls.ToCString());
640 UNREACHABLE();
641 }
642 }
643 }
644 }
645 }
646 }
647 class_map.Release();
648 }
649
650
651 void IsolateReloadContext::VerifyCanonicalTypeArguments() {
652 Thread* thread = Thread::Current();
653 const Array& table =
654 Array::Handle(Z, I->object_store()->canonical_type_arguments());
655 const intptr_t table_size = table.Length() - 1;
656 ASSERT(Utils::IsPowerOfTwo(table_size));
657 TypeArguments& element = TypeArguments::Handle(Z);
658 TypeArguments& other_element = TypeArguments::Handle();
659 for (intptr_t i = 0; i < table_size; i++) {
660 element ^= table.At(i);
661 for (intptr_t j = 0; j < table_size; j++) {
662 if ((i != j) && (table.At(j) != TypeArguments::null())) {
663 other_element ^= table.At(j);
664 if (element.Equals(other_element)) {
665 // Recursive types may be equal, but have different hashes.
666 ASSERT(element.IsRecursive());
667 ASSERT(other_element.IsRecursive());
668 ASSERT(element.Hash() != other_element.Hash());
669 }
670 }
671 }
672 }
673 }
674 #endif
675
676
677 void IsolateReloadContext::Commit() {
678 TIMELINE_SCOPE(Commit);
679 TIR_Print("---- COMMITTING REVERSE MAP\n");
680
681 #ifdef DEBUG
682 VerifyMaps();
683 #endif
684
685 {
686 TIMELINE_SCOPE(CopyStaticFieldsAndPatchFieldsAndFunctions);
687 // Copy static field values from the old classes to the new classes.
688 // Patch fields and functions in the old classes so that they retain
689 // the old script.
690 Class& cls = Class::Handle();
691 Class& new_cls = Class::Handle();
692
693 UnorderedHashMap<ClassMapTraits> class_map(class_map_storage_);
694
695 {
696 UnorderedHashMap<ClassMapTraits>::Iterator it(&class_map);
697 while (it.MoveNext()) {
698 const intptr_t entry = it.Current();
699 new_cls = Class::RawCast(class_map.GetKey(entry));
700 cls = Class::RawCast(class_map.GetPayload(entry, 0));
701 if (new_cls.raw() != cls.raw()) {
702 ASSERT(new_cls.is_enum_class() == cls.is_enum_class());
703 if (new_cls.is_enum_class() && new_cls.is_finalized()) {
704 new_cls.ReplaceEnum(cls);
705 }
706 new_cls.CopyStaticFieldValues(cls);
707 cls.PatchFieldsAndFunctions();
708 }
709 }
710 }
711
712 class_map.Release();
713 }
714
715 // Copy over certain properties of libraries, e.g. is the library
716 // debuggable?
717 {
718 TIMELINE_SCOPE(CopyLibraryBits);
719 Library& lib = Library::Handle();
720 Library& new_lib = Library::Handle();
721
722 UnorderedHashMap<LibraryMapTraits> lib_map(library_map_storage_);
723
724 {
725 // Reload existing libraries.
726 UnorderedHashMap<LibraryMapTraits>::Iterator it(&lib_map);
727
728 while (it.MoveNext()) {
729 const intptr_t entry = it.Current();
730 ASSERT(entry != -1);
731 new_lib = Library::RawCast(lib_map.GetKey(entry));
732 lib = Library::RawCast(lib_map.GetPayload(entry, 0));
733 new_lib.set_debuggable(lib.IsDebuggable());
734 }
735 }
736
737 // Release the library map.
738 lib_map.Release();
739 }
740
741 {
742 TIMELINE_SCOPE(UpdateLibrariesArray);
743 // Update the libraries array.
744 Library& lib = Library::Handle();
745 const GrowableObjectArray& libs = GrowableObjectArray::Handle(
746 I->object_store()->libraries());
747 for (intptr_t i = 0; i < libs.Length(); i++) {
748 lib = Library::RawCast(libs.At(i));
749 TIR_Print("Lib '%s' at index %" Pd "\n", lib.ToCString(), i);
750 lib.set_index(i);
751 }
752
753 // Initialize library side table.
754 library_infos_.SetLength(libs.Length());
755 for (intptr_t i = 0; i < libs.Length(); i++) {
756 lib = Library::RawCast(libs.At(i));
757 // Mark the library dirty if it comes after the libraries we saved.
758 library_infos_[i].dirty = i >= num_saved_libs_;
759 }
760 }
761
762 {
763 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
764 intptr_t replacement_count = become_map.NumOccupied();
765 const Array& before =
766 Array::Handle(Array::New(replacement_count, Heap::kOld));
767 const Array& after =
768 Array::Handle(Array::New(replacement_count, Heap::kOld));
769 Object& obj = Object::Handle();
770 intptr_t replacement_index = 0;
771 UnorderedHashMap<BecomeMapTraits>::Iterator it(&become_map);
772 while (it.MoveNext()) {
773 const intptr_t entry = it.Current();
774 obj = become_map.GetKey(entry);
775 before.SetAt(replacement_index, obj);
776 obj = become_map.GetPayload(entry, 0);
777 after.SetAt(replacement_index, obj);
778 replacement_index++;
779 }
780 ASSERT(replacement_index == replacement_count);
781 become_map.Release();
782
783 Become::ElementsForwardIdentity(before, after);
784 }
785
786 if (FLAG_identity_reload) {
787 if (saved_num_cids_ != I->class_table()->NumCids()) {
788 TIR_Print("Identity reload failed! B#C=%" Pd " A#C=%" Pd "\n",
789 saved_num_cids_,
790 I->class_table()->NumCids());
791 }
792 const GrowableObjectArray& saved_libs =
793 GrowableObjectArray::Handle(saved_libraries());
794 const GrowableObjectArray& libs =
795 GrowableObjectArray::Handle(I->object_store()->libraries());
796 if (saved_libs.Length() != libs.Length()) {
797 TIR_Print("Identity reload failed! B#L=%" Pd " A#L=%" Pd "\n",
798 saved_libs.Length(),
799 libs.Length());
800 }
801 }
802
803 #ifdef DEBUG
804 // TODO(turnidge): Remove before committing to main branch.
805 VerifyCanonicalTypeArguments();
806 #endif
807 }
808
809
810 bool IsolateReloadContext::IsDirty(const Library& lib) {
811 const intptr_t index = lib.index();
812 if (index == static_cast<classid_t>(-1)) {
813 // Treat deleted libraries as dirty.
814 return true;
815 }
816 ASSERT((index >= 0) && (index < library_infos_.length()));
817 return library_infos_[index].dirty;
818 }
819
820
821 void IsolateReloadContext::PostCommit() {
822 TIMELINE_SCOPE(PostCommit);
823 set_saved_root_library(Library::Handle());
824 set_saved_libraries(GrowableObjectArray::Handle());
825 InvalidateWorld();
826 }
827
828
829 bool IsolateReloadContext::ValidateReload() {
830 TIMELINE_SCOPE(ValidateReload);
831 if (has_error_) {
832 return false;
833 }
834
835 // Already built.
836 ASSERT(class_map_storage_ != Array::null());
837 UnorderedHashMap<ClassMapTraits> map(class_map_storage_);
838 UnorderedHashMap<ClassMapTraits>::Iterator it(&map);
839 Class& cls = Class::Handle();
840 Class& new_cls = Class::Handle();
841 while (it.MoveNext()) {
842 const intptr_t entry = it.Current();
843 new_cls = Class::RawCast(map.GetKey(entry));
844 cls = Class::RawCast(map.GetPayload(entry, 0));
845 if (new_cls.raw() != cls.raw()) {
846 if (!cls.CanReload(new_cls)) {
847 map.Release();
848 return false;
849 }
850 }
851 }
852 map.Release();
853 return true;
854 }
855
856
857 RawClass* IsolateReloadContext::FindOriginalClass(const Class& cls) {
858 return MappedClass(cls);
859 }
860
861
862 RawClass* IsolateReloadContext::GetClassForHeapWalkAt(intptr_t cid) {
863 if (saved_class_table_ != NULL) {
864 ASSERT(cid > 0);
865 ASSERT(cid < saved_num_cids_);
866 return saved_class_table_[cid];
867 } else {
868 return isolate_->class_table()->At(cid);
869 }
870 }
871
872
873 RawLibrary* IsolateReloadContext::saved_root_library() const {
874 return saved_root_library_;
875 }
876
877
878 void IsolateReloadContext::set_saved_root_library(const Library& value) {
879 saved_root_library_ = value.raw();
880 }
881
882
883 RawGrowableObjectArray* IsolateReloadContext::saved_libraries() const {
884 return saved_libraries_;
885 }
886
887
888 void IsolateReloadContext::set_saved_libraries(
889 const GrowableObjectArray& value) {
890 saved_libraries_ = value.raw();
891 }
892
893
894 void IsolateReloadContext::VisitObjectPointers(ObjectPointerVisitor* visitor) {
895 visitor->VisitPointers(from(), to());
896 if (saved_class_table_ != NULL) {
897 visitor->VisitPointers(
898 reinterpret_cast<RawObject**>(&saved_class_table_[0]), saved_num_cids_);
899 }
900 }
901
902
903 ObjectStore* IsolateReloadContext::object_store() {
904 return isolate_->object_store();
905 }
906
907
908 static void ResetICs(const Function& function, const Code& code) {
909 // TODO(johnmccutchan): Relying on the function's ICData Map can miss ICDatas.
910 // Use the code's object pool instead.
911 if (function.ic_data_array() == Array::null()) {
912 // TODO(johnmccutchan): Even in this case, we need to scan the code's object
913 // pool instead.
914 return; // Already reset in an earlier round.
915 }
916
917 Thread* thread = Thread::Current();
918 Zone* zone = thread->zone();
919
920 ZoneGrowableArray<const ICData*>* ic_data_array =
921 new(zone) ZoneGrowableArray<const ICData*>();
922 function.RestoreICDataMap(ic_data_array, false /* clone ic-data */);
923 const intptr_t ic_data_array_length = ic_data_array->length();
924 if (ic_data_array_length == 0) {
925 return;
926 }
927 const PcDescriptors& descriptors =
928 PcDescriptors::Handle(code.pc_descriptors());
929 PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kIcCall |
930 RawPcDescriptors::kUnoptStaticCall);
931 while (iter.MoveNext()) {
932 const intptr_t index = iter.DeoptId();
933 if (index >= ic_data_array_length) {
934 // TODO(johnmccutchan): Investigate how this can happen.
935 continue;
936 }
937 const ICData* ic_data = (*ic_data_array)[index];
938 if (ic_data == NULL) {
939 // TODO(johnmccutchan): Investigate how this can happen.
940 continue;
941 }
942 bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall;
943 ic_data->Reset(is_static_call);
944 }
945 }
946
947
948 void IsolateReloadContext::ResetUnoptimizedICsOnStack() {
949 Code& code = Code::Handle();
950 Function& function = Function::Handle();
951 DartFrameIterator iterator;
952 StackFrame* frame = iterator.NextFrame();
953 while (frame != NULL) {
954 code = frame->LookupDartCode();
955 if (code.is_optimized()) {
956 // If this code is optimized, we need to reset the ICs in the
957 // corresponding unoptimized code, which will be executed when the stack
958 // unwinds to the the optimized code.
959 function = code.function();
960 code = function.unoptimized_code();
961 ASSERT(!code.IsNull());
962 ResetICs(function, code);
963 } else {
964 function = code.function();
965 ResetICs(function, code);
966 }
967 frame = iterator.NextFrame();
968 }
969 }
970
971
972 void IsolateReloadContext::ResetMegamorphicCaches() {
973 object_store()->set_megamorphic_cache_table(GrowableObjectArray::Handle());
974 // Since any current optimized code will not make any more calls, it may be
975 // better to clear the table instead of clearing each of the caches, allow
976 // the current megamorphic caches get GC'd and any new optimized code allocate
977 // new ones.
978 }
979
980
981 class MarkFunctionsForRecompilation : public ObjectVisitor {
982 public:
983 MarkFunctionsForRecompilation(Isolate* isolate,
984 IsolateReloadContext* reload_context)
985 : ObjectVisitor(),
986 handle_(Object::Handle()),
987 owning_class_(Class::Handle()),
988 owning_lib_(Library::Handle()),
989 code_(Code::Handle()),
990 reload_context_(reload_context) {
991 }
992
993 virtual void VisitObject(RawObject* obj) {
994 // Free-list elements cannot even be wrapped in handles.
995 if (obj->IsFreeListElement()) {
996 return;
997 }
998 handle_ = obj;
999 if (handle_.IsFunction()) {
1000 const Function& func = Function::Cast(handle_);
1001
1002 // Switch to unoptimized code or the lazy compilation stub.
1003 func.SwitchToLazyCompiledUnoptimizedCode();
1004
1005 // Grab the current code.
1006 code_ = func.CurrentCode();
1007 ASSERT(!code_.IsNull());
1008 const bool clear_code = IsFromDirtyLibrary(func);
1009 const bool stub_code = code_.IsStubCode();
1010
1011 // Zero edge counters.
1012 func.ZeroEdgeCounters();
1013
1014 if (!stub_code) {
1015 if (clear_code) {
1016 ClearAllCode(func);
1017 } else {
1018 PreserveUnoptimizedCode(func);
1019 }
1020 }
1021
1022 // Clear counters.
1023 func.set_usage_counter(0);
1024 func.set_deoptimization_counter(0);
1025 func.set_optimized_instruction_count(0);
1026 func.set_optimized_call_site_count(0);
1027 }
1028 }
1029
1030 private:
1031 void ClearAllCode(const Function& func) {
1032 // Null out the ICData array and code.
1033 func.ClearICDataArray();
1034 func.ClearCode();
1035 func.set_was_compiled(false);
1036 }
1037
1038 void PreserveUnoptimizedCode(const Function& func) {
1039 ASSERT(!code_.IsNull());
1040 // We are preserving the unoptimized code, fill all ICData arrays with
1041 // the sentinel values so that we have no stale type feedback.
1042 func.FillICDataWithSentinels(code_);
1043 }
1044
1045 bool IsFromDirtyLibrary(const Function& func) {
1046 owning_class_ = func.Owner();
1047 owning_lib_ = owning_class_.library();
1048 return reload_context_->IsDirty(owning_lib_);
1049 }
1050
1051 Object& handle_;
1052 Class& owning_class_;
1053 Library& owning_lib_;
1054 Code& code_;
1055 IsolateReloadContext* reload_context_;
1056 };
1057
1058
1059 void IsolateReloadContext::MarkAllFunctionsForRecompilation() {
1060 TIMELINE_SCOPE(MarkAllFunctionsForRecompilation);
1061 MarkFunctionsForRecompilation visitor(isolate_, this);
1062 isolate_->heap()->VisitObjects(&visitor);
1063 }
1064
1065
1066 void IsolateReloadContext::InvalidateWorld() {
1067 ResetMegamorphicCaches();
1068 DeoptimizeFunctionsOnStack();
1069 ResetUnoptimizedICsOnStack();
1070
1071 {
1072 NoSafepointScope no_safepoint;
rmacnak 2016/05/13 17:59:34 Push scopes into MarkAllFunctionsForRecompilation
Cutch 2016/05/17 18:03:52 Done.
1073 HeapIterationScope heap_iteration_scope;
1074
1075 MarkAllFunctionsForRecompilation();
1076 }
1077 }
1078
1079
1080 RawClass* IsolateReloadContext::MappedClass(const Class& replacement_or_new) {
1081 UnorderedHashMap<ClassMapTraits> map(class_map_storage_);
1082 Class& cls = Class::Handle();
1083 cls ^= map.GetOrNull(replacement_or_new);
1084 // No need to update storage address because no mutation occurred.
1085 map.Release();
1086 return cls.raw();
1087 }
1088
1089
1090 RawLibrary* IsolateReloadContext::MappedLibrary(
1091 const Library& replacement_or_new) {
1092 return Library::null();
1093 }
1094
1095
1096 RawClass* IsolateReloadContext::OldClassOrNull(
1097 const Class& replacement_or_new) {
1098 UnorderedHashSet<ClassMapTraits> old_classes_set(old_classes_set_storage_);
1099 Class& cls = Class::Handle();
1100 cls ^= old_classes_set.GetOrNull(replacement_or_new);
1101 old_classes_set_storage_ = old_classes_set.Release().raw();
1102 return cls.raw();
1103 }
1104
1105
1106 RawLibrary* IsolateReloadContext::OldLibraryOrNull(
1107 const Library& replacement_or_new) {
1108 UnorderedHashSet<LibraryMapTraits>
1109 old_libraries_set(old_libraries_set_storage_);
1110 Library& lib = Library::Handle();
1111 lib ^= old_libraries_set.GetOrNull(replacement_or_new);
1112 old_libraries_set_storage_ = old_libraries_set.Release().raw();
1113 return lib.raw();
1114 }
1115
1116
1117 void IsolateReloadContext::BuildLibraryMapping() {
1118 const GrowableObjectArray& libs =
1119 GrowableObjectArray::Handle(object_store()->libraries());
1120
1121 Library& replacement_or_new = Library::Handle();
1122 Library& old = Library::Handle();
1123 for (intptr_t i = 0; i < libs.Length(); i++) {
1124 replacement_or_new = Library::RawCast(libs.At(i));
1125 if (IsCleanLibrary(replacement_or_new)) {
1126 continue;
1127 }
1128 old ^= OldLibraryOrNull(replacement_or_new);
1129 if (old.IsNull()) {
1130 // New library.
1131 AddLibraryMapping(replacement_or_new, replacement_or_new);
1132 } else {
1133 ASSERT(!replacement_or_new.is_dart_scheme());
1134 // Replaced class.
1135 AddLibraryMapping(replacement_or_new, old);
1136
1137 AddBecomeMapping(old, replacement_or_new);
1138 }
1139 }
1140 }
1141
1142
1143 void IsolateReloadContext::AddClassMapping(const Class& replacement_or_new,
1144 const Class& original) {
1145 UnorderedHashMap<ClassMapTraits> map(class_map_storage_);
1146 bool update = map.UpdateOrInsert(replacement_or_new, original);
1147 ASSERT(!update);
1148 // The storage given to the map may have been reallocated, remember the new
1149 // address.
1150 class_map_storage_ = map.Release().raw();
1151 }
1152
1153
1154 void IsolateReloadContext::AddLibraryMapping(const Library& replacement_or_new,
1155 const Library& original) {
1156 UnorderedHashMap<LibraryMapTraits> map(library_map_storage_);
1157 bool update = map.UpdateOrInsert(replacement_or_new, original);
1158 ASSERT(!update);
1159 // The storage given to the map may have been reallocated, remember the new
1160 // address.
1161 library_map_storage_ = map.Release().raw();
1162 }
1163
1164
1165 void IsolateReloadContext::AddStaticFieldMapping(
1166 const Field& old_field, const Field& new_field) {
1167 ASSERT(old_field.is_static());
1168 ASSERT(new_field.is_static());
1169
1170 AddBecomeMapping(old_field, new_field);
1171 }
1172
1173
1174 void IsolateReloadContext::AddBecomeMapping(const Object& old,
1175 const Object& neu) {
1176 ASSERT(become_map_storage_ != Array::null());
1177 UnorderedHashMap<BecomeMapTraits> become_map(become_map_storage_);
1178 bool update = become_map.UpdateOrInsert(old, neu);
1179 ASSERT(!update);
1180 become_map_storage_ = become_map.Release().raw();
1181 }
1182
1183
1184 void IsolateReloadContext::RebuildDirectSubclasses() {
1185 ClassTable* class_table = I->class_table();
1186 intptr_t num_cids = class_table->NumCids();
1187
1188 // Clear the direct subclasses for all classes.
1189 Class& cls = Class::Handle();
1190 GrowableObjectArray& subclasses = GrowableObjectArray::Handle();
1191 for (intptr_t i = 1; i < num_cids; i++) {
1192 if (class_table->HasValidClassAt(i)) {
1193 cls = class_table->At(i);
1194 subclasses = cls.direct_subclasses();
1195 if (!subclasses.IsNull()) {
1196 subclasses.SetLength(0);
1197 }
1198 }
1199 }
1200
1201 // Recompute the direct subclasses.
1202 AbstractType& super_type = AbstractType::Handle();
1203 Class& super_cls = Class::Handle();
1204 for (intptr_t i = 1; i < num_cids; i++) {
1205 if (class_table->HasValidClassAt(i)) {
1206 cls = class_table->At(i);
1207 super_type = cls.super_type();
1208 if (!super_type.IsNull() && !super_type.IsObjectType()) {
1209 super_cls = cls.SuperClass();
1210 ASSERT(!super_cls.IsNull());
1211 super_cls.AddDirectSubclass(cls);
1212 }
1213 }
1214 }
1215 }
1216
1217 #endif // !PRODUCT
1218
1219 } // namespace dart
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698