}
+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_;
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.
// 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) {
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();
}
+// 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
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() {"
Handle<JSFunction> 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());
}
+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<String> 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<JSFunction> 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;
}
-// 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) {