From cee59bf522636bcc166663701064d6d9223b3705 Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Tue, 23 Oct 2012 08:25:04 +0000 Subject: [PATCH] Enable incremental code flushing. This enables code flushing even with incremental marking enabled and fully shares the function link field in JSFunctions between candidates for code flushing and the optimized functions list. If a candidate for code flushing gets optimized, it will be evicted from the candidates list. R=ulan@chromium.org BUG=v8:1609 Review URL: https://codereview.chromium.org/11140025 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12796 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/contexts.cc | 12 ++++- src/heap.cc | 8 ++++ src/mark-compact.cc | 46 +++++++++++-------- src/mark-compact.h | 15 ++++--- test/cctest/test-heap.cc | 114 +++++++++++++++++++++++++++++++++++++---------- 5 files changed, 146 insertions(+), 49 deletions(-) diff --git a/src/contexts.cc b/src/contexts.cc index 93c9795..fbb2f1a 100644 --- a/src/contexts.cc +++ b/src/contexts.cc @@ -251,8 +251,6 @@ void Context::AddOptimizedFunction(JSFunction* function) { } } - CHECK(function->next_function_link()->IsUndefined()); - // Check that the context belongs to the weak native contexts list. bool found = false; Object* context = GetHeap()->native_contexts_list(); @@ -265,6 +263,16 @@ void Context::AddOptimizedFunction(JSFunction* function) { } CHECK(found); #endif + + // If the function link field is already used then the function was + // enqueued as a code flushing candidate and we remove it now. + if (!function->next_function_link()->IsUndefined()) { + CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); + flusher->EvictCandidate(function); + } + + ASSERT(function->next_function_link()->IsUndefined()); + function->set_next_function_link(get(OPTIMIZED_FUNCTIONS_LIST)); set(OPTIMIZED_FUNCTIONS_LIST, function); } diff --git a/src/heap.cc b/src/heap.cc index 5502101..0d2cc42 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -420,6 +420,14 @@ void Heap::GarbageCollectionPrologue() { gc_count_++; unflattened_strings_length_ = 0; + bool should_enable_code_flushing = FLAG_flush_code; +#ifdef ENABLE_DEBUGGER_SUPPORT + if (isolate_->debug()->IsLoaded() || isolate_->debug()->has_break_points()) { + should_enable_code_flushing = false; + } +#endif + mark_compact_collector()->EnableCodeFlushing(should_enable_code_flushing); + #ifdef VERIFY_HEAP if (FLAG_verify_heap) { Verify(); diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 24730c6..412bc6b 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -926,6 +926,32 @@ void CodeFlusher::ProcessSharedFunctionInfoCandidates() { } +void CodeFlusher::EvictCandidate(JSFunction* function) { + ASSERT(!function->next_function_link()->IsUndefined()); + Object* undefined = isolate_->heap()->undefined_value(); + + JSFunction* candidate = jsfunction_candidates_head_; + JSFunction* next_candidate; + if (candidate == function) { + next_candidate = GetNextCandidate(function); + jsfunction_candidates_head_ = next_candidate; + ClearNextCandidate(function, undefined); + } else { + while (candidate != NULL) { + next_candidate = GetNextCandidate(candidate); + + if (next_candidate == function) { + next_candidate = GetNextCandidate(function); + SetNextCandidate(candidate, next_candidate); + ClearNextCandidate(function, undefined); + } + + candidate = next_candidate; + } + } +} + + MarkCompactCollector::~MarkCompactCollector() { if (code_flusher_ != NULL) { delete code_flusher_; @@ -1430,21 +1456,8 @@ void MarkCompactCollector::PrepareThreadForCodeFlushing(Isolate* isolate, void MarkCompactCollector::PrepareForCodeFlushing() { ASSERT(heap() == Isolate::Current()->heap()); - // TODO(1609) Currently incremental marker does not support code flushing. - if (!FLAG_flush_code || was_marked_incrementally_) { - EnableCodeFlushing(false); - return; - } - -#ifdef ENABLE_DEBUGGER_SUPPORT - if (heap()->isolate()->debug()->IsLoaded() || - heap()->isolate()->debug()->has_break_points()) { - EnableCodeFlushing(false); - return; - } -#endif - - EnableCodeFlushing(true); + // If code flushing is disabled, there is no need to prepare for it. + if (!is_code_flushing_enabled()) return; // Ensure that empty descriptor array is marked. Method MarkDescriptorArray // relies on it being marked before any other descriptor array. @@ -2005,9 +2018,6 @@ void MarkCompactCollector::AfterMarking() { // Flush code from collected candidates. if (is_code_flushing_enabled()) { code_flusher_->ProcessCandidates(); - // TODO(1609) Currently incremental marker does not support code flushing, - // we need to disable it before incremental marking steps for next cycle. - EnableCodeFlushing(false); } if (!FLAG_watch_ic_patching) { diff --git a/src/mark-compact.h b/src/mark-compact.h index 7c64800..7ab83c4 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -420,17 +420,22 @@ class CodeFlusher { shared_function_info_candidates_head_(NULL) {} void AddCandidate(SharedFunctionInfo* shared_info) { - SetNextCandidate(shared_info, shared_function_info_candidates_head_); - shared_function_info_candidates_head_ = shared_info; + if (GetNextCandidate(shared_info) == NULL) { + SetNextCandidate(shared_info, shared_function_info_candidates_head_); + shared_function_info_candidates_head_ = shared_info; + } } void AddCandidate(JSFunction* function) { ASSERT(function->code() == function->shared()->code()); - ASSERT(function->next_function_link()->IsUndefined()); - SetNextCandidate(function, jsfunction_candidates_head_); - jsfunction_candidates_head_ = function; + if (GetNextCandidate(function)->IsUndefined()) { + SetNextCandidate(function, jsfunction_candidates_head_); + jsfunction_candidates_head_ = function; + } } + void EvictCandidate(JSFunction* function); + void ProcessCandidates() { ProcessSharedFunctionInfoCandidates(); ProcessJSFunctionCandidates(); diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 6927c43..2ccc9d4 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -23,6 +23,19 @@ static void InitializeVM() { } +// Go through all incremental marking steps in one swoop. +static void SimulateIncrementalMarking() { + IncrementalMarking* marking = HEAP->incremental_marking(); + CHECK(marking->IsStopped()); + marking->Start(); + CHECK(marking->IsMarking()); + while (!marking->IsComplete()) { + marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); + } + CHECK(marking->IsComplete()); +} + + static void CheckMap(Map* map, int type, int instance_size) { CHECK(map->IsHeapObject()); #ifdef DEBUG @@ -942,9 +955,9 @@ TEST(Regression39128) { TEST(TestCodeFlushing) { - i::FLAG_allow_natives_syntax = true; // If we do not flush code this test is invalid. if (!FLAG_flush_code) return; + i::FLAG_allow_natives_syntax = true; InitializeVM(); v8::HandleScope scope; const char* source = "function foo() {" @@ -967,18 +980,16 @@ TEST(TestCodeFlushing) { Handle function(JSFunction::cast(func_value)); CHECK(function->shared()->is_compiled()); - // TODO(1609) Currently incremental marker does not support code flushing. - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - + // The code will survive at least two GCs. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); CHECK(function->shared()->is_compiled()); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); - HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + // Simulate several GCs that use full marking. + const int kAgingThreshold = 6; + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + } // foo should no longer be in the compilation cache CHECK(!function->shared()->is_compiled() || function->IsOptimized()); @@ -990,6 +1001,74 @@ TEST(TestCodeFlushing) { } +TEST(TestCodeFlushingIncremental) { + // If we do not flush code this test is invalid. + if (!FLAG_flush_code) return; + i::FLAG_allow_natives_syntax = true; + InitializeVM(); + v8::HandleScope scope; + const char* source = "function foo() {" + " var x = 42;" + " var y = 42;" + " var z = x + y;" + "};" + "foo()"; + Handle foo_name = FACTORY->LookupAsciiSymbol("foo"); + + // This compile will add the code to the compilation cache. + { v8::HandleScope scope; + CompileRun(source); + } + + // Check function is compiled. + Object* func_value = Isolate::Current()->context()->global_object()-> + GetProperty(*foo_name)->ToObjectChecked(); + CHECK(func_value->IsJSFunction()); + Handle function(JSFunction::cast(func_value)); + CHECK(function->shared()->is_compiled()); + + // The code will survive at least two GCs. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled()); + + // Simulate several GCs that use incremental marking. + const int kAgingThreshold = 6; + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->incremental_marking()->Abort(); + SimulateIncrementalMarking(); + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + } + CHECK(!function->shared()->is_compiled() || function->IsOptimized()); + CHECK(!function->is_compiled() || function->IsOptimized()); + + // This compile will compile the function again. + { v8::HandleScope scope; + CompileRun("foo();"); + } + + // Simulate several GCs that use incremental marking but make sure + // the loop breaks once the function is enqueued as a candidate. + for (int i = 0; i < kAgingThreshold; i++) { + HEAP->incremental_marking()->Abort(); + SimulateIncrementalMarking(); + if (!function->next_function_link()->IsUndefined()) break; + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + } + + // Force optimization while incremental marking is active and while + // the function is enqueued as a candidate. + { v8::HandleScope scope; + CompileRun("%OptimizeFunctionOnNextCall(foo); foo();"); + } + + // Simulate one final GC to make sure the candidate queue is sane. + HEAP->CollectAllGarbage(Heap::kNoGCFlags); + CHECK(function->shared()->is_compiled() || !function->IsOptimized()); + CHECK(function->is_compiled() || !function->IsOptimized()); +} + + // Count the number of native contexts in the weak list of native contexts. int CountNativeContexts() { int count = 0; @@ -1767,19 +1846,6 @@ static int CountMapTransitions(Map* map) { } -// Go through all incremental marking steps in one swoop. -static void SimulateIncrementalMarking() { - IncrementalMarking* marking = HEAP->incremental_marking(); - CHECK(marking->IsStopped()); - marking->Start(); - CHECK(marking->IsMarking()); - while (!marking->IsComplete()) { - marking->Step(MB, IncrementalMarking::NO_GC_VIA_STACK_GUARD); - } - CHECK(marking->IsComplete()); -} - - // Test that map transitions are cleared and maps are collected with // incremental marking as well. TEST(Regress1465) { -- 2.7.4