From 365b2eb91e7557fafe4bb78187e77b619170de8f Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Wed, 15 May 2013 16:09:25 +0000 Subject: [PATCH] Preserve optimized code map during GCs weakly. This change preserves the contents of optimized code maps during GCs but treats the references in this cache weakly. It uses infrastructure from code flushing to maintain a list of all caches. R=hpayer@chromium.org Review URL: https://codereview.chromium.org/14794007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14695 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 10 ++-- src/flag-definitions.h | 1 + src/ia32/code-stubs-ia32.cc | 9 ++- src/mark-compact.cc | 110 ++++++++++++++++++++++++++++++++++++ src/mark-compact.h | 36 +++++++++++- src/mips/code-stubs-mips.cc | 10 ++-- src/objects-visiting-inl.h | 22 +++++--- src/objects.cc | 102 ++++++++++++++++++++++----------- src/objects.h | 21 ++++++- src/x64/code-stubs-x64.cc | 8 +-- test/cctest/test-heap.cc | 1 - 11 files changed, 264 insertions(+), 66 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index a7d2b81fce..3521589f88 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -307,8 +307,8 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // The optimized code map must never be empty, so check the first elements. Label install_optimized; // Speculatively move code object into r4. - __ ldr(r4, FieldMemOperand(r1, FixedArray::kHeaderSize + kPointerSize)); - __ ldr(r5, FieldMemOperand(r1, FixedArray::kHeaderSize)); + __ ldr(r4, FieldMemOperand(r1, SharedFunctionInfo::kFirstCodeSlot)); + __ ldr(r5, FieldMemOperand(r1, SharedFunctionInfo::kFirstContextSlot)); __ cmp(r2, r5); __ b(eq, &install_optimized); @@ -317,11 +317,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ ldr(r4, FieldMemOperand(r1, FixedArray::kLengthOffset)); __ bind(&loop); // Do not double check first entry. - - __ cmp(r4, Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength))); + __ cmp(r4, Operand(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex))); __ b(eq, &install_unoptimized); - __ sub(r4, r4, Operand( - Smi::FromInt(SharedFunctionInfo::kEntryLength))); // Skip an entry. + __ sub(r4, r4, Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength))); __ add(r5, r1, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ add(r5, r5, Operand(r4, LSL, kPointerSizeLog2 - kSmiTagSize)); __ ldr(r5, MemOperand(r5)); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 3cffa7bb45..98f1d12064 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -464,6 +464,7 @@ DEFINE_bool(flush_code, true, "flush code that we expect not to use again (during full gc)") DEFINE_bool(flush_code_incrementally, true, "flush code that we expect not to use again (incrementally)") +DEFINE_bool(trace_code_flushing, false, "trace code flushing progress") DEFINE_bool(age_code, true, "track un-executed functions to age code and flush only " "old code") diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 8f26b44336..507aeb6772 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -292,8 +292,8 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Map must never be empty, so check the first elements. Label install_optimized; // Speculatively move code object into edx. - __ mov(edx, FieldOperand(ebx, FixedArray::kHeaderSize + kPointerSize)); - __ cmp(ecx, FieldOperand(ebx, FixedArray::kHeaderSize)); + __ mov(edx, FieldOperand(ebx, SharedFunctionInfo::kFirstCodeSlot)); + __ cmp(ecx, FieldOperand(ebx, SharedFunctionInfo::kFirstContextSlot)); __ j(equal, &install_optimized); // Iterate through the rest of map backwards. edx holds an index as a Smi. @@ -302,10 +302,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ mov(edx, FieldOperand(ebx, FixedArray::kLengthOffset)); __ bind(&loop); // Do not double check first entry. - __ cmp(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength))); + __ cmp(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex))); __ j(equal, &restore); - __ sub(edx, Immediate(Smi::FromInt( - SharedFunctionInfo::kEntryLength))); // Skip an entry. + __ sub(edx, Immediate(Smi::FromInt(SharedFunctionInfo::kEntryLength))); __ cmp(ecx, CodeGenerator::FixedArrayElementOperand(ebx, edx, 0)); __ j(not_equal, &loop, Label::kNear); // Hit: fetch the optimized code. diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 62dee48472..29d8616181 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -1054,6 +1054,70 @@ void CodeFlusher::ProcessSharedFunctionInfoCandidates() { } +void CodeFlusher::ProcessOptimizedCodeMaps() { + static const int kEntriesStart = SharedFunctionInfo::kEntriesStart; + static const int kEntryLength = SharedFunctionInfo::kEntryLength; + static const int kContextOffset = 0; + static const int kCodeOffset = 1; + static const int kLiteralsOffset = 2; + STATIC_ASSERT(kEntryLength == 3); + + SharedFunctionInfo* holder = optimized_code_map_holder_head_; + SharedFunctionInfo* next_holder; + while (holder != NULL) { + next_holder = GetNextCodeMap(holder); + ClearNextCodeMap(holder); + + FixedArray* code_map = FixedArray::cast(holder->optimized_code_map()); + int new_length = kEntriesStart; + int old_length = code_map->length(); + for (int i = kEntriesStart; i < old_length; i += kEntryLength) { + Code* code = Code::cast(code_map->get(i + kCodeOffset)); + MarkBit code_mark = Marking::MarkBitFrom(code); + if (!code_mark.Get()) { + continue; + } + + // Update and record the context slot in the optimizled code map. + Object** context_slot = HeapObject::RawField(code_map, + FixedArray::OffsetOfElementAt(new_length)); + code_map->set(new_length++, code_map->get(i + kContextOffset)); + ASSERT(Marking::IsBlack( + Marking::MarkBitFrom(HeapObject::cast(*context_slot)))); + isolate_->heap()->mark_compact_collector()-> + RecordSlot(context_slot, context_slot, *context_slot); + + // Update and record the code slot in the optimized code map. + Object** code_slot = HeapObject::RawField(code_map, + FixedArray::OffsetOfElementAt(new_length)); + code_map->set(new_length++, code_map->get(i + kCodeOffset)); + ASSERT(Marking::IsBlack( + Marking::MarkBitFrom(HeapObject::cast(*code_slot)))); + isolate_->heap()->mark_compact_collector()-> + RecordSlot(code_slot, code_slot, *code_slot); + + // Update and record the literals slot in the optimized code map. + Object** literals_slot = HeapObject::RawField(code_map, + FixedArray::OffsetOfElementAt(new_length)); + code_map->set(new_length++, code_map->get(i + kLiteralsOffset)); + ASSERT(Marking::IsBlack( + Marking::MarkBitFrom(HeapObject::cast(*literals_slot)))); + isolate_->heap()->mark_compact_collector()-> + RecordSlot(literals_slot, literals_slot, *literals_slot); + } + + // Trim the optimized code map if entries have been removed. + if (new_length < old_length) { + holder->TrimOptimizedCodeMap(old_length - new_length); + } + + holder = next_holder; + } + + optimized_code_map_holder_head_ = NULL; +} + + void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) { // Make sure previous flushing decisions are revisited. isolate_->heap()->incremental_marking()->RecordWrites(shared_info); @@ -1112,6 +1176,36 @@ void CodeFlusher::EvictCandidate(JSFunction* function) { } +void CodeFlusher::EvictOptimizedCodeMap(SharedFunctionInfo* code_map_holder) { + ASSERT(!FixedArray::cast(code_map_holder->optimized_code_map())-> + get(SharedFunctionInfo::kNextMapIndex)->IsUndefined()); + + // Make sure previous flushing decisions are revisited. + isolate_->heap()->incremental_marking()->RecordWrites(code_map_holder); + + SharedFunctionInfo* holder = optimized_code_map_holder_head_; + SharedFunctionInfo* next_holder; + if (holder == code_map_holder) { + next_holder = GetNextCodeMap(code_map_holder); + optimized_code_map_holder_head_ = next_holder; + ClearNextCodeMap(code_map_holder); + } else { + while (holder != NULL) { + next_holder = GetNextCodeMap(holder); + + if (next_holder == code_map_holder) { + next_holder = GetNextCodeMap(code_map_holder); + SetNextCodeMap(holder, next_holder); + ClearNextCodeMap(code_map_holder); + break; + } + + holder = next_holder; + } + } +} + + void CodeFlusher::EvictJSFunctionCandidates() { JSFunction* candidate = jsfunction_candidates_head_; JSFunction* next_candidate; @@ -1136,6 +1230,18 @@ void CodeFlusher::EvictSharedFunctionInfoCandidates() { } +void CodeFlusher::EvictOptimizedCodeMaps() { + SharedFunctionInfo* holder = optimized_code_map_holder_head_; + SharedFunctionInfo* next_holder; + while (holder != NULL) { + next_holder = GetNextCodeMap(holder); + EvictOptimizedCodeMap(holder); + holder = next_holder; + } + ASSERT(optimized_code_map_holder_head_ == NULL); +} + + void CodeFlusher::IteratePointersToFromSpace(ObjectVisitor* v) { Heap* heap = isolate_->heap(); @@ -3955,6 +4061,10 @@ void MarkCompactCollector::EnableCodeFlushing(bool enable) { delete code_flusher_; code_flusher_ = NULL; } + + if (FLAG_trace_code_flushing) { + PrintF("[code-flushing is now %s]\n", enable ? "on" : "off"); + } } diff --git a/src/mark-compact.h b/src/mark-compact.h index cbc8f410c6..0f20440062 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -406,9 +406,10 @@ class SlotsBuffer { // CodeFlusher collects candidates for code flushing during marking and // processes those candidates after marking has completed in order to // reset those functions referencing code objects that would otherwise -// be unreachable. Code objects can be referenced in two ways: +// be unreachable. Code objects can be referenced in three ways: // - SharedFunctionInfo references unoptimized code. // - JSFunction references either unoptimized or optimized code. +// - OptimizedCodeMap references optimized code. // We are not allowed to flush unoptimized code for functions that got // optimized or inlined into optimized code, because we might bailout // into the unoptimized code again during deoptimization. @@ -417,7 +418,8 @@ class CodeFlusher { explicit CodeFlusher(Isolate* isolate) : isolate_(isolate), jsfunction_candidates_head_(NULL), - shared_function_info_candidates_head_(NULL) {} + shared_function_info_candidates_head_(NULL), + optimized_code_map_holder_head_(NULL) {} void AddCandidate(SharedFunctionInfo* shared_info) { if (GetNextCandidate(shared_info) == NULL) { @@ -434,15 +436,25 @@ class CodeFlusher { } } + void AddOptimizedCodeMap(SharedFunctionInfo* code_map_holder) { + if (GetNextCodeMap(code_map_holder)->IsUndefined()) { + SetNextCodeMap(code_map_holder, optimized_code_map_holder_head_); + optimized_code_map_holder_head_ = code_map_holder; + } + } + + void EvictOptimizedCodeMap(SharedFunctionInfo* code_map_holder); void EvictCandidate(SharedFunctionInfo* shared_info); void EvictCandidate(JSFunction* function); void ProcessCandidates() { + ProcessOptimizedCodeMaps(); ProcessSharedFunctionInfoCandidates(); ProcessJSFunctionCandidates(); } void EvictAllCandidates() { + EvictOptimizedCodeMaps(); EvictJSFunctionCandidates(); EvictSharedFunctionInfoCandidates(); } @@ -450,8 +462,10 @@ class CodeFlusher { void IteratePointersToFromSpace(ObjectVisitor* v); private: + void ProcessOptimizedCodeMaps(); void ProcessJSFunctionCandidates(); void ProcessSharedFunctionInfoCandidates(); + void EvictOptimizedCodeMaps(); void EvictJSFunctionCandidates(); void EvictSharedFunctionInfoCandidates(); @@ -489,9 +503,27 @@ class CodeFlusher { candidate->code()->set_gc_metadata(NULL, SKIP_WRITE_BARRIER); } + static SharedFunctionInfo* GetNextCodeMap(SharedFunctionInfo* holder) { + FixedArray* code_map = FixedArray::cast(holder->optimized_code_map()); + Object* next_map = code_map->get(SharedFunctionInfo::kNextMapIndex); + return reinterpret_cast(next_map); + } + + static void SetNextCodeMap(SharedFunctionInfo* holder, + SharedFunctionInfo* next_holder) { + FixedArray* code_map = FixedArray::cast(holder->optimized_code_map()); + code_map->set(SharedFunctionInfo::kNextMapIndex, next_holder); + } + + static void ClearNextCodeMap(SharedFunctionInfo* holder) { + FixedArray* code_map = FixedArray::cast(holder->optimized_code_map()); + code_map->set_undefined(SharedFunctionInfo::kNextMapIndex); + } + Isolate* isolate_; JSFunction* jsfunction_candidates_head_; SharedFunctionInfo* shared_function_info_candidates_head_; + SharedFunctionInfo* optimized_code_map_holder_head_; DISALLOW_COPY_AND_ASSIGN(CodeFlusher); }; diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index fe33478f74..7c09bb3e93 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -309,8 +309,8 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // The optimized code map must never be empty, so check the first elements. Label install_optimized; // Speculatively move code object into t0. - __ lw(t0, FieldMemOperand(a1, FixedArray::kHeaderSize + kPointerSize)); - __ lw(t1, FieldMemOperand(a1, FixedArray::kHeaderSize)); + __ lw(t0, FieldMemOperand(a1, SharedFunctionInfo::kFirstCodeSlot)); + __ lw(t1, FieldMemOperand(a1, SharedFunctionInfo::kFirstContextSlot)); __ Branch(&install_optimized, eq, a2, Operand(t1)); // Iterate through the rest of map backwards. t0 holds an index as a Smi. @@ -318,11 +318,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ lw(t0, FieldMemOperand(a1, FixedArray::kLengthOffset)); __ bind(&loop); // Do not double check first entry. - __ Branch(&install_unoptimized, eq, t0, - Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength))); - __ Subu(t0, t0, Operand( - Smi::FromInt(SharedFunctionInfo::kEntryLength))); // Skip an entry. + Operand(Smi::FromInt(SharedFunctionInfo::kSecondEntryIndex))); + __ Subu(t0, t0, Operand(Smi::FromInt(SharedFunctionInfo::kEntryLength))); __ Addu(t1, a1, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); __ sll(at, t0, kPointerSizeLog2 - kSmiTagSize); __ Addu(t1, t1, Operand(at)); diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h index d2bf65e3a9..d0a70c3fbf 100644 --- a/src/objects-visiting-inl.h +++ b/src/objects-visiting-inl.h @@ -311,15 +311,17 @@ void StaticMarkingVisitor::VisitSharedFunctionInfo( if (shared->ic_age() != heap->global_ic_age()) { shared->ResetForNewContext(heap->global_ic_age()); } - if (FLAG_cache_optimized_code) { - // Flush optimized code map on major GC. - // TODO(mstarzinger): We may experiment with rebuilding it or with - // retaining entries which should survive as we iterate through - // optimized functions anyway. - shared->ClearOptimizedCodeMap("during full gc"); - } MarkCompactCollector* collector = heap->mark_compact_collector(); if (collector->is_code_flushing_enabled()) { + if (FLAG_cache_optimized_code && !shared->optimized_code_map()->IsSmi()) { + // Add the shared function info holding an optimized code map to + // the code flusher for processing of code maps after marking. + collector->code_flusher()->AddOptimizedCodeMap(shared); + // Treat all references within the code map weakly by marking the + // code map itself but not pushing it onto the marking deque. + FixedArray* code_map = FixedArray::cast(shared->optimized_code_map()); + StaticVisitor::MarkObjectWithoutPush(heap, code_map); + } if (IsFlushable(heap, shared)) { // This function's code looks flushable. But we have to postpone // the decision until we see all functions that point to the same @@ -332,6 +334,12 @@ void StaticMarkingVisitor::VisitSharedFunctionInfo( VisitSharedFunctionInfoWeakCode(heap, object); return; } + } else { + if (FLAG_cache_optimized_code && !shared->optimized_code_map()->IsSmi()) { + // Flush optimized code map on major GCs without code flushing, + // needed because cached code doesn't contain breakpoints. + shared->ClearOptimizedCodeMap(); + } } VisitSharedFunctionInfoStrongCode(heap, object); } diff --git a/src/objects.cc b/src/objects.cc index 53774d788c..b2ad3d4b80 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -2186,7 +2186,7 @@ static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) { Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim); if (trim_mode != FROM_GC || Heap::ShouldZapGarbage()) { - ZapEndOfFixedArray(new_end, to_trim); + ZapEndOfFixedArray(new_end, to_trim); } int size_delta = to_trim * kPointerSize; @@ -8976,33 +8976,46 @@ void SharedFunctionInfo::AddToOptimizedCodeMap( Handle native_context, Handle code, Handle literals) { + CALL_HEAP_FUNCTION_VOID( + shared->GetIsolate(), + shared->AddToOptimizedCodeMap(*native_context, *code, *literals)); +} + + +MaybeObject* SharedFunctionInfo::AddToOptimizedCodeMap(Context* native_context, + Code* code, + FixedArray* literals) { ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); ASSERT(native_context->IsNativeContext()); STATIC_ASSERT(kEntryLength == 3); - Object* value = shared->optimized_code_map(); - Handle new_code_map; + Heap* heap = GetHeap(); + FixedArray* new_code_map; + Object* value = optimized_code_map(); if (value->IsSmi()) { // No optimized code map. ASSERT_EQ(0, Smi::cast(value)->value()); // Crate 3 entries per context {context, code, literals}. - new_code_map = FACTORY->NewFixedArray(kEntryLength); - new_code_map->set(0, *native_context); - new_code_map->set(1, *code); - new_code_map->set(2, *literals); + MaybeObject* maybe = heap->AllocateFixedArray(kInitialLength); + if (!maybe->To(&new_code_map)) return maybe; + new_code_map->set(kEntriesStart + 0, native_context); + new_code_map->set(kEntriesStart + 1, code); + new_code_map->set(kEntriesStart + 2, literals); } else { // Copy old map and append one new entry. - Handle old_code_map(FixedArray::cast(value)); - ASSERT_EQ(-1, shared->SearchOptimizedCodeMap(*native_context)); + FixedArray* old_code_map = FixedArray::cast(value); + ASSERT_EQ(-1, SearchOptimizedCodeMap(native_context)); int old_length = old_code_map->length(); int new_length = old_length + kEntryLength; - new_code_map = FACTORY->NewFixedArray(new_length); - old_code_map->CopyTo(0, *new_code_map, 0, old_length); - new_code_map->set(old_length, *native_context); - new_code_map->set(old_length + 1, *code); - new_code_map->set(old_length + 2, *literals); + MaybeObject* maybe = old_code_map->CopySize(new_length); + if (!maybe->To(&new_code_map)) return maybe; + new_code_map->set(old_length + 0, native_context); + new_code_map->set(old_length + 1, code); + new_code_map->set(old_length + 2, literals); + // Zap the old map for the sake of the heap verifier. + if (Heap::ShouldZapGarbage()) ZapOptimizedCodeMap(); } #ifdef DEBUG - for (int i = 0; i < new_code_map->length(); i += kEntryLength) { + for (int i = kEntriesStart; i < new_code_map->length(); i += kEntryLength) { ASSERT(new_code_map->get(i)->IsNativeContext()); ASSERT(new_code_map->get(i + 1)->IsCode()); ASSERT(Code::cast(new_code_map->get(i + 1))->kind() == @@ -9010,14 +9023,14 @@ void SharedFunctionInfo::AddToOptimizedCodeMap( ASSERT(new_code_map->get(i + 2)->IsFixedArray()); } #endif - shared->set_optimized_code_map(*new_code_map); + set_optimized_code_map(new_code_map); + return new_code_map; } void SharedFunctionInfo::InstallFromOptimizedCodeMap(JSFunction* function, int index) { - ASSERT(index > 0); - ASSERT(optimized_code_map()->IsFixedArray()); + ASSERT(index > kEntriesStart); FixedArray* code_map = FixedArray::cast(optimized_code_map()); if (!bound()) { FixedArray* cached_literals = FixedArray::cast(code_map->get(index + 1)); @@ -9031,15 +9044,18 @@ void SharedFunctionInfo::InstallFromOptimizedCodeMap(JSFunction* function, } -void SharedFunctionInfo::ClearOptimizedCodeMap(const char* reason) { - if (!optimized_code_map()->IsSmi()) { - if (FLAG_trace_opt) { - PrintF("[clearing entire optimizing code map (%s) for ", reason); - ShortPrint(); - PrintF("]\n"); - } - set_optimized_code_map(Smi::FromInt(0)); +void SharedFunctionInfo::ClearOptimizedCodeMap() { + FixedArray* code_map = FixedArray::cast(optimized_code_map()); + + // If the next map link slot is already used then the function was + // enqueued with code flushing and we remove it now. + if (!code_map->get(kNextMapIndex)->IsUndefined()) { + CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher(); + flusher->EvictOptimizedCodeMap(this); } + + ASSERT(code_map->get(kNextMapIndex)->IsUndefined()); + set_optimized_code_map(Smi::FromInt(0)); } @@ -9050,11 +9066,11 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, int i; bool removed_entry = false; FixedArray* code_map = FixedArray::cast(optimized_code_map()); - for (i = 0; i < code_map->length(); i += kEntryLength) { + for (i = kEntriesStart; i < code_map->length(); i += kEntryLength) { ASSERT(code_map->get(i)->IsNativeContext()); if (Code::cast(code_map->get(i + 1)) == optimized_code) { if (FLAG_trace_opt) { - PrintF("[clearing optimizing code map (%s) for ", reason); + PrintF("[evicting entry from optimizing code map (%s) for ", reason); ShortPrint(); PrintF("]\n"); } @@ -9069,15 +9085,35 @@ void SharedFunctionInfo::EvictFromOptimizedCodeMap(Code* optimized_code, i += kEntryLength; } if (removed_entry) { - if (code_map->length() > kEntryLength) { - RightTrimFixedArray(GetHeap(), code_map, kEntryLength); - } else { - ClearOptimizedCodeMap(reason); + // Always trim even when array is cleared because of heap verifier. + RightTrimFixedArray(GetHeap(), code_map, kEntryLength); + if (code_map->length() == kEntriesStart) { + ClearOptimizedCodeMap(); } } } +void SharedFunctionInfo::TrimOptimizedCodeMap(int shrink_by) { + FixedArray* code_map = FixedArray::cast(optimized_code_map()); + ASSERT(shrink_by % kEntryLength == 0); + ASSERT(shrink_by <= code_map->length() - kEntriesStart); + // Always trim even when array is cleared because of heap verifier. + RightTrimFixedArray(GetHeap(), code_map, shrink_by); + if (code_map->length() == kEntriesStart) { + ClearOptimizedCodeMap(); + } +} + + +void SharedFunctionInfo::ZapOptimizedCodeMap() { + FixedArray* code_map = FixedArray::cast(optimized_code_map()); + MemsetPointer(code_map->data_start(), + GetHeap()->the_hole_value(), + code_map->length()); +} + + bool JSFunction::CompileLazy(Handle function, ClearExceptionFlag flag) { bool result = true; @@ -9717,7 +9753,7 @@ int SharedFunctionInfo::SearchOptimizedCodeMap(Context* native_context) { if (!value->IsSmi()) { FixedArray* optimized_code_map = FixedArray::cast(value); int length = optimized_code_map->length(); - for (int i = 0; i < length; i += 3) { + for (int i = kEntriesStart; i < length; i += kEntryLength) { if (optimized_code_map->get(i) == native_context) { return i + 1; } diff --git a/src/objects.h b/src/objects.h index 281672ba4a..6787da5ce8 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5807,7 +5807,7 @@ class SharedFunctionInfo: public HeapObject { inline void ReplaceCode(Code* code); // [optimized_code_map]: Map from native context to optimized code - // and a shared literals array or Smi 0 if none. + // and a shared literals array or Smi(0) if none. DECL_ACCESSORS(optimized_code_map, Object) // Returns index i of the entry with the specified context. At position @@ -5820,17 +5820,34 @@ class SharedFunctionInfo: public HeapObject { void InstallFromOptimizedCodeMap(JSFunction* function, int index); // Clear optimized code map. - void ClearOptimizedCodeMap(const char* reason); + void ClearOptimizedCodeMap(); // Removed a specific optimized code object from the optimized code map. void EvictFromOptimizedCodeMap(Code* optimized_code, const char* reason); + // Trims the optimized code map after entries have been removed. + void TrimOptimizedCodeMap(int shrink_by); + + // Zaps the contents of backing optimized code map. + void ZapOptimizedCodeMap(); + // Add a new entry to the optimized code map. + MUST_USE_RESULT MaybeObject* AddToOptimizedCodeMap(Context* native_context, + Code* code, + FixedArray* literals); static void AddToOptimizedCodeMap(Handle shared, Handle native_context, Handle code, Handle literals); + + // Layout description of the optimized code map. + static const int kNextMapIndex = 0; + static const int kEntriesStart = 1; static const int kEntryLength = 3; + static const int kFirstContextSlot = FixedArray::kHeaderSize + kPointerSize; + static const int kFirstCodeSlot = FixedArray::kHeaderSize + 2 * kPointerSize; + static const int kSecondEntryIndex = kEntryLength + kEntriesStart; + static const int kInitialLength = kEntriesStart + kEntryLength; // [scope_info]: Scope info. DECL_ACCESSORS(scope_info, ScopeInfo) diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 2f4454c150..f87d952568 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -287,8 +287,8 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // The optimized code map must never be empty, so check the first elements. Label install_optimized; // Speculatively move code object into edx. - __ movq(rdx, FieldOperand(rbx, FixedArray::kHeaderSize + kPointerSize)); - __ cmpq(rcx, FieldOperand(rbx, FixedArray::kHeaderSize)); + __ movq(rdx, FieldOperand(rbx, SharedFunctionInfo::kFirstCodeSlot)); + __ cmpq(rcx, FieldOperand(rbx, SharedFunctionInfo::kFirstContextSlot)); __ j(equal, &install_optimized); // Iterate through the rest of map backwards. rdx holds an index. @@ -298,9 +298,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ SmiToInteger32(rdx, rdx); __ bind(&loop); // Do not double check first entry. - __ cmpq(rdx, Immediate(SharedFunctionInfo::kEntryLength)); + __ cmpq(rdx, Immediate(SharedFunctionInfo::kSecondEntryIndex)); __ j(equal, &restore); - __ subq(rdx, Immediate(SharedFunctionInfo::kEntryLength)); // Skip an entry. + __ subq(rdx, Immediate(SharedFunctionInfo::kEntryLength)); __ cmpq(rcx, FieldOperand(rbx, rdx, times_pointer_size, diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 647303520d..ca173c25a5 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -2814,7 +2814,6 @@ TEST(Regress169209) { CcTest::InitializeVM(); Isolate* isolate = Isolate::Current(); - // Force experimental natives to compile to normalize heap layout. Heap* heap = isolate->heap(); HandleScope scope(isolate);