Index: src/deoptimizer.cc |
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc |
index 49d046bb6ab4165a928ac35873fe2015b17b63d7..40beda85fcbc24608cda1349ce3824709e258c62 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,161 @@ 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); |
+ ZoneList<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(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_; |
-}; |
+ // TODO(titzer): we need a handle scope only because of the macro assembler, |
+ // which is only used in EnsureCodeForDeoptimizationEntry. |
+ HandleScope scope(isolate); |
+ // 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. |
+ 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); |
+ 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); |
+ 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); |
+ } 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()); |
} |
-#endif |
} |
@@ -647,8 +583,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 +700,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. |
+ 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; |
} |
@@ -3121,22 +3063,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. |