| 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.
|
|
|