Chromium Code Reviews| Index: src/deoptimizer.cc |
| diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc |
| index dc9ffc51186be1c5535297c6adf77f9efbf922b1..96f753fb70de2b1909b08fc9dfd77f515ef3b37b 100644 |
| --- a/src/deoptimizer.cc |
| +++ b/src/deoptimizer.cc |
| @@ -56,11 +56,10 @@ static MemoryChunk* AllocateCodeChunk(MemoryAllocator* allocator) { |
| DeoptimizerData::DeoptimizerData(MemoryAllocator* allocator) |
| : allocator_(allocator), |
| - current_(NULL), |
| #ifdef ENABLE_DEBUGGER_SUPPORT |
| deoptimized_frame_info_(NULL), |
| #endif |
| - deoptimizing_code_list_(NULL) { |
| + current_(NULL) { |
| for (int i = 0; i < Deoptimizer::kBailoutTypesWithCodeEntry; ++i) { |
| deopt_entry_code_entries_[i] = -1; |
| deopt_entry_code_[i] = AllocateCodeChunk(allocator); |
| @@ -73,14 +72,6 @@ DeoptimizerData::~DeoptimizerData() { |
| allocator_->Free(deopt_entry_code_[i]); |
| deopt_entry_code_[i] = NULL; |
| } |
| - |
| - DeoptimizingCodeListNode* current = deoptimizing_code_list_; |
| - while (current != NULL) { |
| - DeoptimizingCodeListNode* prev = current; |
| - current = current->next(); |
| - delete prev; |
| - } |
| - deoptimizing_code_list_ = NULL; |
| } |
| @@ -93,33 +84,19 @@ void DeoptimizerData::Iterate(ObjectVisitor* v) { |
| #endif |
| -Code* DeoptimizerData::FindDeoptimizingCode(Address addr) { |
| - for (DeoptimizingCodeListNode* node = deoptimizing_code_list_; |
| - node != NULL; |
| - node = node->next()) { |
| - if (node->code()->contains(addr)) return *node->code(); |
| - } |
| - return NULL; |
| -} |
| - |
| - |
| -void DeoptimizerData::RemoveDeoptimizingCode(Code* code) { |
| - for (DeoptimizingCodeListNode *prev = NULL, *cur = deoptimizing_code_list_; |
| - cur != NULL; |
| - prev = cur, cur = cur->next()) { |
| - if (*cur->code() == code) { |
| - if (prev == NULL) { |
| - deoptimizing_code_list_ = cur->next(); |
| - } else { |
| - prev->set_next(cur->next()); |
| - } |
| - delete cur; |
| - return; |
| +Code* Deoptimizer::FindDeoptimizingCode(Address addr) { |
| + if (function_->IsHeapObject()) { |
| + // Search all deoptimizing code in the native context of the function. |
| + Context* native_context = function_->context()->native_context(); |
| + Object* element = native_context->DeoptimizedCodeListHead(); |
| + while (!element->IsUndefined()) { |
| + Code* code = Code::cast(element); |
| + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); |
| + if (code->contains(addr)) return code; |
| + element = code->next_code_link(); |
| } |
| } |
| - // Deoptimizing code is removed through weak callback. Each object is expected |
| - // to be removed once and only once. |
| - UNREACHABLE(); |
| + return NULL; |
| } |
| @@ -289,27 +266,42 @@ void Deoptimizer::GenerateDeoptimizationEntries(MacroAssembler* masm, |
| void Deoptimizer::VisitAllOptimizedFunctionsForContext( |
| Context* context, OptimizedFunctionVisitor* visitor) { |
| - Isolate* isolate = context->GetIsolate(); |
| - Zone zone(isolate); |
| DisallowHeapAllocation no_allocation; |
| ASSERT(context->IsNativeContext()); |
| visitor->EnterContext(context); |
| - // Create a snapshot of the optimized functions list. This is needed because |
| - // visitors might remove more than one link from the list at once. |
| - ZoneList<JSFunction*> snapshot(1, &zone); |
| + // Visit the list of optimized functions, removing elements that |
| + // no longer refer to optimized code. |
| + JSFunction* prev = NULL; |
| Object* element = context->OptimizedFunctionsListHead(); |
| while (!element->IsUndefined()) { |
| - JSFunction* element_function = JSFunction::cast(element); |
| - snapshot.Add(element_function, &zone); |
| - element = element_function->next_function_link(); |
| - } |
| - |
| - // Run through the snapshot of optimized functions and visit them. |
| - for (int i = 0; i < snapshot.length(); ++i) { |
| - visitor->VisitFunction(snapshot.at(i)); |
| + JSFunction* function = JSFunction::cast(element); |
| + Object* next = function->next_function_link(); |
| + if (function->code()->kind() != Code::OPTIMIZED_FUNCTION || |
| + (visitor->VisitFunction(function), |
| + function->code()->kind() != Code::OPTIMIZED_FUNCTION)) { |
| + // The function no longer refers to optimized code, or the visitor |
| + // changed the code to which it refers to no longer be optimized code. |
| + // Remove the function from this list. |
| + if (prev != NULL) { |
| + prev->set_next_function_link(next); |
| + } else { |
| + context->SetOptimizedFunctionsListHead(next); |
| + } |
| + // The visitor should not alter the link directly. |
| + ASSERT(function->next_function_link() == next); |
| + // Set the next function link to undefined to indicate it is no longer |
| + // in the optimized functions list. |
| + function->set_next_function_link(context->GetHeap()->undefined_value()); |
| + } else { |
| + // The visitor should not alter the link directly. |
| + ASSERT(function->next_function_link() == next); |
| + // preserve this element. |
| + prev = function; |
| + } |
| + element = next; |
| } |
| visitor->LeaveContext(context); |
| @@ -321,7 +313,7 @@ void Deoptimizer::VisitAllOptimizedFunctions( |
| OptimizedFunctionVisitor* visitor) { |
| DisallowHeapAllocation no_allocation; |
| - // Run through the list of all native contexts and deoptimize. |
| + // Run through the list of all native contexts. |
| Object* context = isolate->heap()->native_contexts_list(); |
| while (!context->IsUndefined()) { |
| VisitAllOptimizedFunctionsForContext(Context::cast(context), visitor); |
| @@ -330,217 +322,160 @@ void Deoptimizer::VisitAllOptimizedFunctions( |
| } |
| -// Removes the functions selected by the given filter from the optimized |
| -// function list of the given context and adds their code to the list of |
| -// code objects to be deoptimized. |
| -static void SelectCodeToDeoptimize(Context* context, |
| - OptimizedFunctionFilter* filter, |
| - ZoneList<Code*>* codes, |
| - Zone* zone, |
| - Object* undefined) { |
| +// Unlink functions referring to code marked for deoptimization, then move |
| +// marked code from the optimized code list to the deoptimized code list, |
| +// and patch code for lazy deopt. |
| +void Deoptimizer::DeoptimizeMarkedCodeForContext(Context* context) { |
| DisallowHeapAllocation no_allocation; |
| - Object* current = context->get(Context::OPTIMIZED_FUNCTIONS_LIST); |
| - Object* remainder_head = undefined; |
| - Object* remainder_tail = undefined; |
| - |
| - // TODO(titzer): rewrite to not modify unselected functions. |
| - while (current != undefined) { |
| - JSFunction* function = JSFunction::cast(current); |
| - current = function->next_function_link(); |
| - if (filter->TakeFunction(function)) { |
| - // Extract this function from the context's list and remember the code. |
| + |
| + // A "closure" that unlinks optimized code that is going to be |
| + // deoptimized from the functions that refer to it. |
| + class SelectedCodeUnlinker: public OptimizedFunctionVisitor { |
| + public: |
| + virtual void EnterContext(Context* context) { } // Don't care. |
| + virtual void LeaveContext(Context* context) { } // Don't care. |
| + virtual void VisitFunction(JSFunction* function) { |
| Code* code = function->code(); |
| - ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); |
| - if (code->marked_for_deoptimization()) { |
| - ASSERT(codes->Contains(code)); |
| - } else { |
| - code->set_marked_for_deoptimization(true); |
| - codes->Add(code, zone); |
| - } |
| + if (!code->marked_for_deoptimization()) return; |
| + |
| + // Unlink this function and evict from optimized code map. |
| SharedFunctionInfo* shared = function->shared(); |
| - // Replace the function's code with the shared code. |
| function->set_code(shared->code()); |
| - // Evict the code from the optimized code map. |
| shared->EvictFromOptimizedCodeMap(code, "deoptimized function"); |
| - // Remove the function from the optimized functions list. |
| - function->set_next_function_link(undefined); |
| if (FLAG_trace_deopt) { |
| - PrintF("[forced deoptimization: "); |
| + PrintF("[deoptimizer unlinked: "); |
| function->PrintName(); |
| PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function)); |
| } |
| - } else { |
| - // Don't select this function; link it back into the list. |
| - if (remainder_head == undefined) { |
| - remainder_head = function; |
| - } else { |
| - JSFunction::cast(remainder_tail)->set_next_function_link(function); |
| - } |
| - remainder_tail = function; |
| } |
| - } |
| - if (remainder_tail != undefined) { |
| - JSFunction::cast(remainder_tail)->set_next_function_link(undefined); |
| - } |
| - context->set(Context::OPTIMIZED_FUNCTIONS_LIST, remainder_head); |
| -} |
| + }; |
| + // Unlink all functions that refer to marked code. |
| + SelectedCodeUnlinker unlinker; |
| + VisitAllOptimizedFunctionsForContext(context, &unlinker); |
| -class DeoptimizeAllFilter : public OptimizedFunctionFilter { |
| - public: |
| - virtual bool TakeFunction(JSFunction* function) { |
| - return true; |
| - } |
| -}; |
| + // Move marked code from the optimized code list to the deoptimized |
| + // code list, collecting them into a ZoneList. |
| + Isolate* isolate = context->GetHeap()->isolate(); |
| + Zone zone(isolate); |
| + HandleScope scope(isolate); |
| + ZoneList<Handle<Code> > codes(10, &zone); |
| + // Walk over all optimized code objects in this native context. |
| + Code* prev = NULL; |
| + Object* element = context->OptimizedCodeListHead(); |
| + while (!element->IsUndefined()) { |
| + Code* code = Code::cast(element); |
| + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); |
| + Object* next = code->next_code_link(); |
| + if (code->marked_for_deoptimization()) { |
| + // Put the code into the list for later patching. |
| + codes.Add(Handle<Code>(code), &zone); |
| + |
| + if (prev != NULL) { |
| + // Skip this code in the optimized code list. |
| + prev->set_next_code_link(next); |
| + } else { |
| + // There was no previous node, the next node is the new head. |
| + context->SetOptimizedCodeListHead(next); |
| + } |
| -class DeoptimizeWithMatchingCodeFilter : public OptimizedFunctionFilter { |
| - public: |
| - explicit DeoptimizeWithMatchingCodeFilter(Code* code) : code_(code) {} |
| - virtual bool TakeFunction(JSFunction* function) { |
| - return function->code() == code_; |
| + // Move the code to the _deoptimized_ code list. |
| + code->set_next_code_link(context->DeoptimizedCodeListHead()); |
| + context->SetDeoptimizedCodeListHead(code); |
| + } else { |
| + // Not marked; preserve this element. |
| + prev = code; |
| + } |
| + element = next; |
| } |
| - private: |
| - Code* code_; |
| -}; |
| + // Now patch all the codes for deoptimization. |
| + for (int i = 0; i < codes.length(); i++) { |
| + // It is finally time to die, code object. |
| + // Do platform-specific patching to force any activations to lazy deopt. |
| + // (This may actually cause a GC due to a stub being generated.) |
|
Michael Starzinger
2013/09/03 22:03:47
Is this actually true? Can this cause a GC, I cann
titzer
2013/09/04 11:19:04
Right...fixed. No GC tolerated.
|
| + PatchCodeForDeoptimization(isolate, *codes[i]); |
| -class DeoptimizeMarkedCodeFilter : public OptimizedFunctionFilter { |
| - public: |
| - virtual bool TakeFunction(JSFunction* function) { |
| - return function->code()->marked_for_deoptimization(); |
| + // We might be in the middle of incremental marking with compaction. |
| + // Tell collector to treat this code object in a special way and |
| + // ignore all slots that might have been recorded on it. |
| + isolate->heap()->mark_compact_collector()->InvalidateCode(*codes[i]); |
| } |
| -}; |
| +} |
| void Deoptimizer::DeoptimizeAll(Isolate* isolate) { |
| - DisallowHeapAllocation no_allocation; |
| - |
| if (FLAG_trace_deopt) { |
| - PrintF("[deoptimize all contexts]\n"); |
| + PrintF("[deoptimize all code in all contexts]\n"); |
| } |
| - |
| - DeoptimizeAllFilter filter; |
| - DeoptimizeAllFunctionsWith(isolate, &filter); |
| -} |
| - |
| - |
| -void Deoptimizer::DeoptimizeGlobalObject(JSObject* object) { |
| DisallowHeapAllocation no_allocation; |
| - DeoptimizeAllFilter filter; |
| - if (object->IsJSGlobalProxy()) { |
| - Object* proto = object->GetPrototype(); |
| - ASSERT(proto->IsJSGlobalObject()); |
| - DeoptimizeAllFunctionsForContext( |
| - GlobalObject::cast(proto)->native_context(), &filter); |
| - } else if (object->IsGlobalObject()) { |
| - DeoptimizeAllFunctionsForContext( |
| - GlobalObject::cast(object)->native_context(), &filter); |
| + // For all contexts, mark all code, then deoptimize. |
| + Object* context = isolate->heap()->native_contexts_list(); |
| + while (!context->IsUndefined()) { |
| + Context* native_context = Context::cast(context); |
| + MarkAllCodeForContext(native_context); |
| + DeoptimizeMarkedCodeForContext(native_context); |
|
Michael Starzinger
2013/09/03 21:52:31
Since DeoptimizeMarkedCodeForContext() might cause
titzer
2013/09/04 11:19:04
So; you were right, we don't tolerate GC here--tha
|
| + context = native_context->get(Context::NEXT_CONTEXT_LINK); |
| } |
| } |
| -void Deoptimizer::DeoptimizeFunction(JSFunction* function) { |
| - Code* code = function->code(); |
| - if (code->kind() != Code::OPTIMIZED_FUNCTION) return; |
| - DeoptimizeWithMatchingCodeFilter filter(code); |
| - DeoptimizeAllFunctionsForContext( |
| - function->context()->native_context(), &filter); |
| -} |
| - |
| - |
| -void Deoptimizer::DeoptimizeAllFunctionsForContext( |
| - Context* context, OptimizedFunctionFilter* filter) { |
| - ASSERT(context->IsNativeContext()); |
| - Isolate* isolate = context->GetIsolate(); |
| - Object* undefined = isolate->heap()->undefined_value(); |
| - Zone zone(isolate); |
| - ZoneList<Code*> codes(4, &zone); |
| - SelectCodeToDeoptimize(context, filter, &codes, &zone, undefined); |
| - for (int i = 0; i < codes.length(); i++) { |
| - DeoptimizeCode(isolate, codes.at(i)); |
| +void Deoptimizer::DeoptimizeMarkedCode(Isolate* isolate) { |
| + if (FLAG_trace_deopt) { |
| + PrintF("[deoptimize marked code in all contexts]\n"); |
| } |
| -} |
| - |
| - |
| -void Deoptimizer::DeoptimizeAllFunctionsWith(Isolate* isolate, |
| - OptimizedFunctionFilter* filter) { |
| DisallowHeapAllocation no_allocation; |
| - |
| - // Run through the list of all native contexts and deoptimize. |
| + // For all contexts, deoptimize code already marked. |
| Object* context = isolate->heap()->native_contexts_list(); |
| while (!context->IsUndefined()) { |
| - DeoptimizeAllFunctionsForContext(Context::cast(context), filter); |
| - context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); |
| + Context* native_context = Context::cast(context); |
| + DeoptimizeMarkedCodeForContext(native_context); |
|
Michael Starzinger
2013/09/03 21:52:31
Likewise.
|
| + context = native_context->get(Context::NEXT_CONTEXT_LINK); |
| } |
| } |
| -void Deoptimizer::DeoptimizeCodeList(Isolate* isolate, ZoneList<Code*>* codes) { |
| - if (codes->length() == 0) return; // Nothing to do. |
| - |
| - // Mark the code; any functions refering to this code will be selected. |
| - for (int i = 0; i < codes->length(); i++) { |
| - ASSERT(!codes->at(i)->marked_for_deoptimization()); |
| - codes->at(i)->set_marked_for_deoptimization(true); |
| - } |
| - |
| - // For all contexts, remove optimized functions that refer to the selected |
| - // code from the optimized function lists. |
| - Object* undefined = isolate->heap()->undefined_value(); |
| - Zone zone(isolate); |
| - Object* list = isolate->heap()->native_contexts_list(); |
| - DeoptimizeMarkedCodeFilter filter; |
| - while (!list->IsUndefined()) { |
| - Context* context = Context::cast(list); |
| - // Note that selecting code unlinks the functions that refer to it. |
| - SelectCodeToDeoptimize(context, &filter, codes, &zone, undefined); |
| - list = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); |
| +void Deoptimizer::DeoptimizeGlobalObject(JSObject* object) { |
| + if (FLAG_trace_deopt) { |
| + PrintF("[deoptimize global object @ 0x%08" V8PRIxPTR "]\n", |
| + reinterpret_cast<intptr_t>(object)); |
| } |
| - |
| - // Now deoptimize all the code. |
| - for (int i = 0; i < codes->length(); i++) { |
| - DeoptimizeCode(isolate, codes->at(i)); |
| + if (object->IsJSGlobalProxy()) { |
| + Object* proto = object->GetPrototype(); |
| + ASSERT(proto->IsJSGlobalObject()); |
| + Context* native_context = GlobalObject::cast(proto)->native_context(); |
| + MarkAllCodeForContext(native_context); |
| + DeoptimizeMarkedCodeForContext(native_context); |
|
Michael Starzinger
2013/09/03 21:52:31
Likewise.
|
| + } else if (object->IsGlobalObject()) { |
| + Context* native_context = GlobalObject::cast(object)->native_context(); |
| + MarkAllCodeForContext(native_context); |
| + DeoptimizeMarkedCodeForContext(native_context); |
| } |
| } |
| -void Deoptimizer::DeoptimizeCode(Isolate* isolate, Code* code) { |
| - HandleScope scope(isolate); |
| - DisallowHeapAllocation nha; |
| - |
| - // Do platform-specific patching of the optimized code. |
| - PatchCodeForDeoptimization(isolate, code); |
| - |
| - // Add the deoptimizing code to the list. |
| - DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code); |
| - DeoptimizerData* data = isolate->deoptimizer_data(); |
| - node->set_next(data->deoptimizing_code_list_); |
| - data->deoptimizing_code_list_ = node; |
| - |
| - // We might be in the middle of incremental marking with compaction. |
| - // Tell collector to treat this code object in a special way and |
| - // ignore all slots that might have been recorded on it. |
| - isolate->heap()->mark_compact_collector()->InvalidateCode(code); |
| +void Deoptimizer::MarkAllCodeForContext(Context* context) { |
| + Object* element = context->OptimizedCodeListHead(); |
| + while (!element->IsUndefined()) { |
| + Code* code = Code::cast(element); |
| + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); |
| + code->set_marked_for_deoptimization(true); |
| + element = code->next_code_link(); |
| + } |
| } |
| -void Deoptimizer::HandleWeakDeoptimizedCode(v8::Isolate* isolate, |
| - v8::Persistent<v8::Value>* obj, |
| - void* parameter) { |
| - DeoptimizingCodeListNode* node = |
| - reinterpret_cast<DeoptimizingCodeListNode*>(parameter); |
| - DeoptimizerData* data = |
| - reinterpret_cast<Isolate*>(isolate)->deoptimizer_data(); |
| - data->RemoveDeoptimizingCode(*node->code()); |
| -#ifdef DEBUG |
| - for (DeoptimizingCodeListNode* current = data->deoptimizing_code_list_; |
| - current != NULL; |
| - current = current->next()) { |
| - ASSERT(current != node); |
| +void Deoptimizer::DeoptimizeFunction(JSFunction* function) { |
| + Code* code = function->code(); |
| + if (code->kind() == Code::OPTIMIZED_FUNCTION) { |
| + // Mark the code for deoptimization and unlink any functions that also |
| + // refer to that code. The code cannot be shared across native contexts, |
| + // so we only need to search one. |
| + code->set_marked_for_deoptimization(true); |
| + DeoptimizeMarkedCodeForContext(function->context()->native_context()); |
|
Michael Starzinger
2013/09/03 21:52:31
Likewise.
|
| } |
| -#endif |
| } |
| @@ -647,8 +582,7 @@ Code* Deoptimizer::FindOptimizedCode(JSFunction* function, |
| case Deoptimizer::SOFT: |
| case Deoptimizer::EAGER: |
| case Deoptimizer::LAZY: { |
| - Code* compiled_code = |
| - isolate_->deoptimizer_data()->FindDeoptimizingCode(from_); |
| + Code* compiled_code = FindDeoptimizingCode(from_); |
| return (compiled_code == NULL) |
| ? static_cast<Code*>(isolate_->FindCodeObject(from_)) |
| : compiled_code; |
| @@ -765,11 +699,18 @@ int Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data, |
| int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) { |
| int length = 0; |
| - DeoptimizingCodeListNode* node = |
| - isolate->deoptimizer_data()->deoptimizing_code_list_; |
| - while (node != NULL) { |
| - length++; |
| - node = node->next(); |
| + // count all entries in the deoptimizing code list of every context. |
|
Michael Starzinger
2013/09/03 21:52:31
nit: Capitalize sentence.
titzer
2013/09/04 11:19:04
Done.
|
| + Object* context = isolate->heap()->native_contexts_list(); |
| + while (!context->IsUndefined()) { |
| + Context* native_context = Context::cast(context); |
| + Object* element = native_context->DeoptimizedCodeListHead(); |
| + while (!element->IsUndefined()) { |
| + Code* code = Code::cast(element); |
| + ASSERT(code->kind() == Code::OPTIMIZED_FUNCTION); |
| + length++; |
| + element = code->next_code_link(); |
| + } |
| + context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); |
| } |
| return length; |
| } |
| @@ -3103,22 +3044,6 @@ const char* Translation::StringFor(Opcode opcode) { |
| #endif |
| -DeoptimizingCodeListNode::DeoptimizingCodeListNode(Code* code): next_(NULL) { |
| - GlobalHandles* global_handles = code->GetIsolate()->global_handles(); |
| - // Globalize the code object and make it weak. |
| - code_ = Handle<Code>::cast(global_handles->Create(code)); |
| - global_handles->MakeWeak(reinterpret_cast<Object**>(code_.location()), |
| - this, |
| - Deoptimizer::HandleWeakDeoptimizedCode); |
| -} |
| - |
| - |
| -DeoptimizingCodeListNode::~DeoptimizingCodeListNode() { |
| - GlobalHandles* global_handles = code_->GetIsolate()->global_handles(); |
| - global_handles->Destroy(reinterpret_cast<Object**>(code_.location())); |
| -} |
| - |
| - |
| // We can't intermix stack decoding and allocations because |
| // deoptimization infrastracture is not GC safe. |
| // Thus we build a temporary structure in malloced space. |