diff options
Diffstat (limited to 'src/3rdparty/v8/src/deoptimizer.cc')
-rw-r--r-- | src/3rdparty/v8/src/deoptimizer.cc | 615 |
1 files changed, 489 insertions, 126 deletions
diff --git a/src/3rdparty/v8/src/deoptimizer.cc b/src/3rdparty/v8/src/deoptimizer.cc index 2a30ddd..9d16211 100644 --- a/src/3rdparty/v8/src/deoptimizer.cc +++ b/src/3rdparty/v8/src/deoptimizer.cc @@ -27,6 +27,7 @@ #include "v8.h" +#include "accessors.h" #include "codegen.h" #include "deoptimizer.h" #include "disasm.h" @@ -40,8 +41,11 @@ namespace v8 { namespace internal { DeoptimizerData::DeoptimizerData() { - eager_deoptimization_entry_code_ = NULL; - lazy_deoptimization_entry_code_ = NULL; + eager_deoptimization_entry_code_entries_ = -1; + lazy_deoptimization_entry_code_entries_ = -1; + size_t deopt_table_size = Deoptimizer::GetMaxDeoptTableSize(); + eager_deoptimization_entry_code_ = new VirtualMemory(deopt_table_size); + lazy_deoptimization_entry_code_ = new VirtualMemory(deopt_table_size); current_ = NULL; deoptimizing_code_list_ = NULL; #ifdef ENABLE_DEBUGGER_SUPPORT @@ -51,16 +55,18 @@ DeoptimizerData::DeoptimizerData() { DeoptimizerData::~DeoptimizerData() { - if (eager_deoptimization_entry_code_ != NULL) { - Isolate::Current()->memory_allocator()->Free( - eager_deoptimization_entry_code_); - eager_deoptimization_entry_code_ = NULL; - } - if (lazy_deoptimization_entry_code_ != NULL) { - Isolate::Current()->memory_allocator()->Free( - lazy_deoptimization_entry_code_); - lazy_deoptimization_entry_code_ = NULL; + delete eager_deoptimization_entry_code_; + eager_deoptimization_entry_code_ = NULL; + delete lazy_deoptimization_entry_code_; + lazy_deoptimization_entry_code_ = NULL; + + DeoptimizingCodeListNode* current = deoptimizing_code_list_; + while (current != NULL) { + DeoptimizingCodeListNode* prev = current; + current = current->next(); + delete prev; } + deoptimizing_code_list_ = NULL; } @@ -95,6 +101,20 @@ Deoptimizer* Deoptimizer::New(JSFunction* function, } +// No larger than 2K on all platforms +static const int kDeoptTableMaxEpilogueCodeSize = 2 * KB; + + +size_t Deoptimizer::GetMaxDeoptTableSize() { + int entries_size = + Deoptimizer::kMaxNumberOfEntries * Deoptimizer::table_entry_size_; + int commit_page_size = static_cast<int>(OS::CommitPageSize()); + int page_count = ((kDeoptTableMaxEpilogueCodeSize + entries_size - 1) / + commit_page_size) + 1; + return static_cast<size_t>(commit_page_size * page_count); +} + + Deoptimizer* Deoptimizer::Grab(Isolate* isolate) { ASSERT(isolate == Isolate::Current()); Deoptimizer* result = isolate->deoptimizer_data()->current_; @@ -268,20 +288,29 @@ void Deoptimizer::DeoptimizeGlobalObject(JSObject* object) { void Deoptimizer::VisitAllOptimizedFunctionsForContext( Context* context, OptimizedFunctionVisitor* visitor) { + Isolate* isolate = context->GetIsolate(); + ZoneScope zone_scope(isolate->runtime_zone(), DELETE_ON_EXIT); AssertNoAllocation no_allocation; - ASSERT(context->IsGlobalContext()); + ASSERT(context->IsNativeContext()); visitor->EnterContext(context); - // Run through the list of optimized functions and deoptimize them. + + // 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, isolate->runtime_zone()); Object* element = context->OptimizedFunctionsListHead(); while (!element->IsUndefined()) { JSFunction* element_function = JSFunction::cast(element); - // Get the next link before deoptimizing as deoptimizing will clear the - // next link. + snapshot.Add(element_function, isolate->runtime_zone()); element = element_function->next_function_link(); - visitor->VisitFunction(element_function); } + + // Run through the snapshot of optimized functions and visit them. + for (int i = 0; i < snapshot.length(); ++i) { + visitor->VisitFunction(snapshot.at(i)); + } + visitor->LeaveContext(context); } @@ -294,10 +323,10 @@ void Deoptimizer::VisitAllOptimizedFunctionsForGlobalObject( Object* proto = object->GetPrototype(); ASSERT(proto->IsJSGlobalObject()); VisitAllOptimizedFunctionsForContext( - GlobalObject::cast(proto)->global_context(), visitor); + GlobalObject::cast(proto)->native_context(), visitor); } else if (object->IsGlobalObject()) { VisitAllOptimizedFunctionsForContext( - GlobalObject::cast(object)->global_context(), visitor); + GlobalObject::cast(object)->native_context(), visitor); } } @@ -306,12 +335,12 @@ void Deoptimizer::VisitAllOptimizedFunctions( OptimizedFunctionVisitor* visitor) { AssertNoAllocation no_allocation; - // Run through the list of all global contexts and deoptimize. - Object* context = Isolate::Current()->heap()->global_contexts_list(); + // Run through the list of all native contexts and deoptimize. + Object* context = Isolate::Current()->heap()->native_contexts_list(); while (!context->IsUndefined()) { // GC can happen when the context is not fully initialized, // so the global field of the context can be undefined. - Object* global = Context::cast(context)->get(Context::GLOBAL_INDEX); + Object* global = Context::cast(context)->get(Context::GLOBAL_OBJECT_INDEX); if (!global->IsUndefined()) { VisitAllOptimizedFunctionsForGlobalObject(JSObject::cast(global), visitor); @@ -354,10 +383,13 @@ Deoptimizer::Deoptimizer(Isolate* isolate, bailout_type_(type), from_(from), fp_to_sp_delta_(fp_to_sp_delta), + has_alignment_padding_(0), input_(NULL), output_count_(0), jsframe_count_(0), output_(NULL), + deferred_arguments_objects_values_(0), + deferred_arguments_objects_(0), deferred_heap_numbers_(0) { if (FLAG_trace_deopt && type != OSR) { if (type == DEBUGGER) { @@ -378,6 +410,7 @@ Deoptimizer::Deoptimizer(Isolate* isolate, reinterpret_cast<intptr_t>(from), fp_to_sp_delta - (2 * kPointerSize)); } + function->shared()->increment_deopt_count(); // Find the optimized code. if (type == EAGER) { ASSERT(from == NULL); @@ -440,61 +473,61 @@ void Deoptimizer::DeleteFrameDescriptions() { } -Address Deoptimizer::GetDeoptimizationEntry(int id, BailoutType type) { +Address Deoptimizer::GetDeoptimizationEntry(int id, + BailoutType type, + GetEntryMode mode) { ASSERT(id >= 0); - if (id >= kNumberOfEntries) return NULL; - MemoryChunk* base = NULL; + if (id >= kMaxNumberOfEntries) return NULL; + VirtualMemory* base = NULL; + if (mode == ENSURE_ENTRY_CODE) { + EnsureCodeForDeoptimizationEntry(type, id); + } else { + ASSERT(mode == CALCULATE_ENTRY_ADDRESS); + } DeoptimizerData* data = Isolate::Current()->deoptimizer_data(); if (type == EAGER) { - if (data->eager_deoptimization_entry_code_ == NULL) { - data->eager_deoptimization_entry_code_ = CreateCode(type); - } base = data->eager_deoptimization_entry_code_; } else { - if (data->lazy_deoptimization_entry_code_ == NULL) { - data->lazy_deoptimization_entry_code_ = CreateCode(type); - } base = data->lazy_deoptimization_entry_code_; } return - static_cast<Address>(base->area_start()) + (id * table_entry_size_); + static_cast<Address>(base->address()) + (id * table_entry_size_); } int Deoptimizer::GetDeoptimizationId(Address addr, BailoutType type) { - MemoryChunk* base = NULL; + VirtualMemory* base = NULL; DeoptimizerData* data = Isolate::Current()->deoptimizer_data(); if (type == EAGER) { base = data->eager_deoptimization_entry_code_; } else { base = data->lazy_deoptimization_entry_code_; } + Address base_casted = reinterpret_cast<Address>(base->address()); if (base == NULL || - addr < base->area_start() || - addr >= base->area_start() + - (kNumberOfEntries * table_entry_size_)) { + addr < base->address() || + addr >= base_casted + (kMaxNumberOfEntries * table_entry_size_)) { return kNotDeoptimizationEntry; } ASSERT_EQ(0, - static_cast<int>(addr - base->area_start()) % table_entry_size_); - return static_cast<int>(addr - base->area_start()) / table_entry_size_; + static_cast<int>(addr - base_casted) % table_entry_size_); + return static_cast<int>(addr - base_casted) / table_entry_size_; } int Deoptimizer::GetOutputInfo(DeoptimizationOutputData* data, - unsigned id, + BailoutId id, SharedFunctionInfo* shared) { // TODO(kasperl): For now, we do a simple linear search for the PC // offset associated with the given node id. This should probably be // changed to a binary search. int length = data->DeoptPoints(); - Smi* smi_id = Smi::FromInt(id); for (int i = 0; i < length; i++) { - if (data->AstId(i) == smi_id) { + if (data->AstId(i) == id) { return data->PcAndState(i)->value(); } } - PrintF("[couldn't find pc offset for node=%u]\n", id); + PrintF("[couldn't find pc offset for node=%d]\n", id.ToInt()); PrintF("[method: %s]\n", *shared->DebugName()->ToCString()); // Print the source code if available. HeapStringAllocator string_allocator; @@ -541,7 +574,7 @@ void Deoptimizer::DoComputeOutputFrames() { // described by the input data. DeoptimizationInputData* input_data = DeoptimizationInputData::cast(optimized_code_->deoptimization_data()); - unsigned node_id = input_data->AstId(bailout_id_)->value(); + BailoutId node_id = input_data->AstId(bailout_id_); ByteArray* translations = input_data->TranslationByteArray(); unsigned translation_index = input_data->TranslationIndex(bailout_id_)->value(); @@ -579,7 +612,24 @@ void Deoptimizer::DoComputeOutputFrames() { case Translation::CONSTRUCT_STUB_FRAME: DoComputeConstructStubFrame(&iterator, i); break; - default: + case Translation::GETTER_STUB_FRAME: + DoComputeAccessorStubFrame(&iterator, i, false); + break; + case Translation::SETTER_STUB_FRAME: + DoComputeAccessorStubFrame(&iterator, i, true); + break; + case Translation::BEGIN: + case Translation::REGISTER: + case Translation::INT32_REGISTER: + case Translation::UINT32_REGISTER: + case Translation::DOUBLE_REGISTER: + case Translation::STACK_SLOT: + case Translation::INT32_STACK_SLOT: + case Translation::UINT32_STACK_SLOT: + case Translation::DOUBLE_STACK_SLOT: + case Translation::LITERAL: + case Translation::ARGUMENTS_OBJECT: + case Translation::DUPLICATE: UNREACHABLE(); break; } @@ -593,19 +643,34 @@ void Deoptimizer::DoComputeOutputFrames() { PrintF("[deoptimizing: end 0x%08" V8PRIxPTR " ", reinterpret_cast<intptr_t>(function)); function->PrintName(); - PrintF(" => node=%u, pc=0x%08" V8PRIxPTR ", state=%s, took %0.3f ms]\n", - node_id, + PrintF(" => node=%d, pc=0x%08" V8PRIxPTR ", state=%s, alignment=%s," + " took %0.3f ms]\n", + node_id.ToInt(), output_[index]->GetPc(), FullCodeGenerator::State2String( static_cast<FullCodeGenerator::State>( output_[index]->GetState()->value())), + has_alignment_padding_ ? "with padding" : "no padding", ms); } } -void Deoptimizer::MaterializeHeapNumbers() { +void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { ASSERT_NE(DEBUGGER, bailout_type_); + + // Handlify all argument object values before triggering any allocation. + List<Handle<Object> > values(deferred_arguments_objects_values_.length()); + for (int i = 0; i < deferred_arguments_objects_values_.length(); ++i) { + values.Add(Handle<Object>(deferred_arguments_objects_values_[i])); + } + + // Play it safe and clear all unhandlified values before we continue. + deferred_arguments_objects_values_.Clear(); + + // Materialize all heap numbers before looking at arguments because when the + // output frames are used to materialize arguments objects later on they need + // to already contain valid heap numbers. for (int i = 0; i < deferred_heap_numbers_.length(); i++) { HeapNumberMaterializationDescriptor d = deferred_heap_numbers_[i]; Handle<Object> num = isolate_->factory()->NewNumber(d.value()); @@ -615,9 +680,55 @@ void Deoptimizer::MaterializeHeapNumbers() { d.value(), d.slot_address()); } - Memory::Object_at(d.slot_address()) = *num; } + + // Materialize arguments objects one frame at a time. + for (int frame_index = 0; frame_index < jsframe_count(); ++frame_index) { + if (frame_index != 0) it->Advance(); + JavaScriptFrame* frame = it->frame(); + Handle<JSFunction> function(JSFunction::cast(frame->function()), isolate_); + Handle<JSObject> arguments; + for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) { + if (frame->GetExpression(i) == isolate_->heap()->arguments_marker()) { + ArgumentsObjectMaterializationDescriptor descriptor = + deferred_arguments_objects_.RemoveLast(); + const int length = descriptor.arguments_length(); + if (arguments.is_null()) { + if (frame->has_adapted_arguments()) { + // Use the arguments adapter frame we just built to materialize the + // arguments object. FunctionGetArguments can't throw an exception, + // so cast away the doubt with an assert. + arguments = Handle<JSObject>(JSObject::cast( + Accessors::FunctionGetArguments(*function, + NULL)->ToObjectUnchecked())); + values.RewindBy(length); + } else { + // Construct an arguments object and copy the parameters to a newly + // allocated arguments object backing store. + arguments = + isolate_->factory()->NewArgumentsObject(function, length); + Handle<FixedArray> array = + isolate_->factory()->NewFixedArray(length); + ASSERT(array->length() == length); + for (int i = length - 1; i >= 0 ; --i) { + array->set(i, *values.RemoveLast()); + } + arguments->set_elements(*array); + } + } + frame->SetExpression(i, *arguments); + ASSERT_EQ(Memory::Object_at(descriptor.slot_address()), *arguments); + if (FLAG_trace_deopt) { + PrintF("Materializing %sarguments object for %p: ", + frame->has_adapted_arguments() ? "(adapted) " : "", + reinterpret_cast<void*>(descriptor.slot_address())); + arguments->ShortPrint(); + PrintF("\n"); + } + } + } + } } @@ -696,6 +807,8 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::CONSTRUCT_STUB_FRAME: + case Translation::GETTER_STUB_FRAME: + case Translation::SETTER_STUB_FRAME: case Translation::DUPLICATE: UNREACHABLE(); return; @@ -744,6 +857,34 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, return; } + case Translation::UINT32_REGISTER: { + int input_reg = iterator->Next(); + uintptr_t value = static_cast<uintptr_t>(input_->GetRegister(input_reg)); + bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue)); + if (FLAG_trace_deopt) { + PrintF( + " 0x%08" V8PRIxPTR ": [top + %d] <- %" V8PRIuPTR + " ; uint %s (%s)\n", + output_[frame_index]->GetTop() + output_offset, + output_offset, + value, + converter.NameOfCPURegister(input_reg), + is_smi ? "smi" : "heap number"); + } + if (is_smi) { + intptr_t tagged_value = + reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value))); + output_[frame_index]->SetFrameSlot(output_offset, tagged_value); + } else { + // We save the untagged value on the side and store a GC-safe + // temporary placeholder in the frame. + AddDoubleValue(output_[frame_index]->GetTop() + output_offset, + static_cast<double>(static_cast<uint32_t>(value))); + output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder); + } + return; + } + case Translation::DOUBLE_REGISTER: { int input_reg = iterator->Next(); double value = input_->GetDoubleRegister(input_reg); @@ -769,7 +910,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": ", output_[frame_index]->GetTop() + output_offset); - PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d] ", + PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [sp + %d] ", output_offset, input_value, input_offset); @@ -789,7 +930,7 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": ", output_[frame_index]->GetTop() + output_offset); - PrintF("[top + %d] <- %" V8PRIdPTR " ; [esp + %d] (%s)\n", + PrintF("[top + %d] <- %" V8PRIdPTR " ; [sp + %d] (%s)\n", output_offset, value, input_offset, @@ -809,13 +950,43 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, return; } + case Translation::UINT32_STACK_SLOT: { + int input_slot_index = iterator->Next(); + unsigned input_offset = + input_->GetOffsetFromSlotIndex(input_slot_index); + uintptr_t value = + static_cast<uintptr_t>(input_->GetFrameSlot(input_offset)); + bool is_smi = (value <= static_cast<uintptr_t>(Smi::kMaxValue)); + if (FLAG_trace_deopt) { + PrintF(" 0x%08" V8PRIxPTR ": ", + output_[frame_index]->GetTop() + output_offset); + PrintF("[top + %d] <- %" V8PRIuPTR " ; [sp + %d] (uint32 %s)\n", + output_offset, + value, + input_offset, + is_smi ? "smi" : "heap number"); + } + if (is_smi) { + intptr_t tagged_value = + reinterpret_cast<intptr_t>(Smi::FromInt(static_cast<int>(value))); + output_[frame_index]->SetFrameSlot(output_offset, tagged_value); + } else { + // We save the untagged value on the side and store a GC-safe + // temporary placeholder in the frame. + AddDoubleValue(output_[frame_index]->GetTop() + output_offset, + static_cast<double>(static_cast<uint32_t>(value))); + output_[frame_index]->SetFrameSlot(output_offset, kPlaceholder); + } + return; + } + case Translation::DOUBLE_STACK_SLOT: { int input_slot_index = iterator->Next(); unsigned input_offset = input_->GetOffsetFromSlotIndex(input_slot_index); double value = input_->GetDoubleFrameSlot(input_offset); if (FLAG_trace_deopt) { - PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [esp + %d]\n", + PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- %e ; [sp + %d]\n", output_[frame_index]->GetTop() + output_offset, output_offset, value, @@ -843,8 +1014,8 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, } case Translation::ARGUMENTS_OBJECT: { - // Use the arguments marker value as a sentinel and fill in the arguments - // object after the deoptimized frame is built. + int args_index = iterator->Next() + 1; // Skip receiver. + int args_length = iterator->Next() - 1; // Skip receiver. if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- ", output_[frame_index]->GetTop() + output_offset, @@ -852,15 +1023,76 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, isolate_->heap()->arguments_marker()->ShortPrint(); PrintF(" ; arguments object\n"); } + // Use the arguments marker value as a sentinel and fill in the arguments + // object after the deoptimized frame is built. intptr_t value = reinterpret_cast<intptr_t>( isolate_->heap()->arguments_marker()); + AddArgumentsObject( + output_[frame_index]->GetTop() + output_offset, args_length); output_[frame_index]->SetFrameSlot(output_offset, value); + // We save the tagged argument values on the side and materialize the + // actual arguments object after the deoptimized frame is built. + for (int i = 0; i < args_length; i++) { + unsigned input_offset = input_->GetOffsetFromSlotIndex(args_index + i); + intptr_t input_value = input_->GetFrameSlot(input_offset); + AddArgumentsObjectValue(input_value); + } return; } } } +static bool ObjectToInt32(Object* obj, int32_t* value) { + if (obj->IsSmi()) { + *value = Smi::cast(obj)->value(); + return true; + } + + if (obj->IsHeapNumber()) { + double num = HeapNumber::cast(obj)->value(); + if (FastI2D(FastD2I(num)) != num) { + if (FLAG_trace_osr) { + PrintF("**** %g could not be converted to int32 ****\n", + HeapNumber::cast(obj)->value()); + } + return false; + } + + *value = FastD2I(num); + return true; + } + + return false; +} + + +static bool ObjectToUint32(Object* obj, uint32_t* value) { + if (obj->IsSmi()) { + if (Smi::cast(obj)->value() < 0) return false; + + *value = static_cast<uint32_t>(Smi::cast(obj)->value()); + return true; + } + + if (obj->IsHeapNumber()) { + double num = HeapNumber::cast(obj)->value(); + if ((num < 0) || (FastUI2D(FastD2UI(num)) != num)) { + if (FLAG_trace_osr) { + PrintF("**** %g could not be converted to uint32 ****\n", + HeapNumber::cast(obj)->value()); + } + return false; + } + + *value = FastD2UI(num); + return true; + } + + return false; +} + + bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, int* input_offset) { disasm::NameConverter converter; @@ -883,6 +1115,8 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::CONSTRUCT_STUB_FRAME: + case Translation::GETTER_STUB_FRAME: + case Translation::SETTER_STUB_FRAME: case Translation::DUPLICATE: UNREACHABLE(); // Malformed input. return false; @@ -900,22 +1134,10 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, } case Translation::INT32_REGISTER: { - // Abort OSR if we don't have a number. - if (!input_object->IsNumber()) return false; + int32_t int32_value = 0; + if (!ObjectToInt32(input_object, &int32_value)) return false; int output_reg = iterator->Next(); - int int32_value = input_object->IsSmi() - ? Smi::cast(input_object)->value() - : FastD2I(input_object->Number()); - // Abort the translation if the conversion lost information. - if (!input_object->IsSmi() && - FastI2D(int32_value) != input_object->Number()) { - if (FLAG_trace_osr) { - PrintF("**** %g could not be converted to int32 ****\n", - input_object->Number()); - } - return false; - } if (FLAG_trace_osr) { PrintF(" %s <- %d (int32) ; [sp + %d]\n", converter.NameOfCPURegister(output_reg), @@ -926,6 +1148,21 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, break; } + case Translation::UINT32_REGISTER: { + uint32_t uint32_value = 0; + if (!ObjectToUint32(input_object, &uint32_value)) return false; + + int output_reg = iterator->Next(); + if (FLAG_trace_osr) { + PrintF(" %s <- %u (uint32) ; [sp + %d]\n", + converter.NameOfCPURegister(output_reg), + uint32_value, + *input_offset); + } + output->SetRegister(output_reg, static_cast<int32_t>(uint32_value)); + } + + case Translation::DOUBLE_REGISTER: { // Abort OSR if we don't have a number. if (!input_object->IsNumber()) return false; @@ -959,24 +1196,12 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, } case Translation::INT32_STACK_SLOT: { - // Abort OSR if we don't have a number. - if (!input_object->IsNumber()) return false; + int32_t int32_value = 0; + if (!ObjectToInt32(input_object, &int32_value)) return false; int output_index = iterator->Next(); unsigned output_offset = output->GetOffsetFromSlotIndex(output_index); - int int32_value = input_object->IsSmi() - ? Smi::cast(input_object)->value() - : DoubleToInt32(input_object->Number()); - // Abort the translation if the conversion lost information. - if (!input_object->IsSmi() && - FastI2D(int32_value) != input_object->Number()) { - if (FLAG_trace_osr) { - PrintF("**** %g could not be converted to int32 ****\n", - input_object->Number()); - } - return false; - } if (FLAG_trace_osr) { PrintF(" [sp + %d] <- %d (int32) ; [sp + %d]\n", output_offset, @@ -987,6 +1212,23 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator, break; } + case Translation::UINT32_STACK_SLOT: { + uint32_t uint32_value = 0; + if (!ObjectToUint32(input_object, &uint32_value)) return false; + + int output_index = iterator->Next(); + unsigned output_offset = + output->GetOffsetFromSlotIndex(output_index); + if (FLAG_trace_osr) { + PrintF(" [sp + %d] <- %u (uint32) ; [sp + %d]\n", + output_offset, + uint32_value, + *input_offset); + } + output->SetFrameSlot(output_offset, static_cast<int32_t>(uint32_value)); + break; + } + case Translation::DOUBLE_STACK_SLOT: { static const int kLowerOffset = 0 * kPointerSize; static const int kUpperOffset = 1 * kPointerSize; @@ -1136,39 +1378,63 @@ Object* Deoptimizer::ComputeLiteral(int index) const { } -void Deoptimizer::AddDoubleValue(intptr_t slot_address, - double value) { +void Deoptimizer::AddArgumentsObject(intptr_t slot_address, int argc) { + ArgumentsObjectMaterializationDescriptor object_desc( + reinterpret_cast<Address>(slot_address), argc); + deferred_arguments_objects_.Add(object_desc); +} + + +void Deoptimizer::AddArgumentsObjectValue(intptr_t value) { + deferred_arguments_objects_values_.Add(reinterpret_cast<Object*>(value)); +} + + +void Deoptimizer::AddDoubleValue(intptr_t slot_address, double value) { HeapNumberMaterializationDescriptor value_desc( reinterpret_cast<Address>(slot_address), value); deferred_heap_numbers_.Add(value_desc); } -MemoryChunk* Deoptimizer::CreateCode(BailoutType type) { +void Deoptimizer::EnsureCodeForDeoptimizationEntry(BailoutType type, + int max_entry_id) { // We cannot run this if the serializer is enabled because this will // cause us to emit relocation information for the external // references. This is fine because the deoptimizer's code section // isn't meant to be serialized at all. ASSERT(!Serializer::enabled()); + ASSERT(type == EAGER || type == LAZY); + DeoptimizerData* data = Isolate::Current()->deoptimizer_data(); + int entry_count = (type == EAGER) + ? data->eager_deoptimization_entry_code_entries_ + : data->lazy_deoptimization_entry_code_entries_; + if (max_entry_id < entry_count) return; + entry_count = Min(Max(entry_count * 2, Deoptimizer::kMinNumberOfEntries), + Deoptimizer::kMaxNumberOfEntries); + MacroAssembler masm(Isolate::Current(), NULL, 16 * KB); masm.set_emit_debug_code(false); - GenerateDeoptimizationEntries(&masm, kNumberOfEntries, type); + GenerateDeoptimizationEntries(&masm, entry_count, type); CodeDesc desc; masm.GetCode(&desc); ASSERT(desc.reloc_size == 0); - MemoryChunk* chunk = - Isolate::Current()->memory_allocator()->AllocateChunk(desc.instr_size, - EXECUTABLE, - NULL); - ASSERT(chunk->area_size() >= desc.instr_size); - if (chunk == NULL) { - V8::FatalProcessOutOfMemory("Not enough memory for deoptimization table"); + VirtualMemory* memory = type == EAGER + ? data->eager_deoptimization_entry_code_ + : data->lazy_deoptimization_entry_code_; + size_t table_size = Deoptimizer::GetMaxDeoptTableSize(); + ASSERT(static_cast<int>(table_size) >= desc.instr_size); + memory->Commit(memory->address(), table_size, true); + memcpy(memory->address(), desc.buffer, desc.instr_size); + CPU::FlushICache(memory->address(), desc.instr_size); + + if (type == EAGER) { + data->eager_deoptimization_entry_code_entries_ = entry_count; + } else { + data->lazy_deoptimization_entry_code_entries_ = entry_count; } - memcpy(chunk->area_start(), desc.buffer, desc.instr_size); - CPU::FlushICache(chunk->area_start(), desc.instr_size); - return chunk; } @@ -1210,6 +1476,54 @@ void Deoptimizer::RemoveDeoptimizingCode(Code* code) { } +static Object* CutOutRelatedFunctionsList(Context* context, + Code* code, + Object* undefined) { + Object* result_list_head = undefined; + Object* head; + Object* current; + current = head = context->get(Context::OPTIMIZED_FUNCTIONS_LIST); + JSFunction* prev = NULL; + while (current != undefined) { + JSFunction* func = JSFunction::cast(current); + current = func->next_function_link(); + if (func->code() == code) { + func->set_next_function_link(result_list_head); + result_list_head = func; + if (prev) { + prev->set_next_function_link(current); + } else { + head = current; + } + } else { + prev = func; + } + } + if (head != context->get(Context::OPTIMIZED_FUNCTIONS_LIST)) { + context->set(Context::OPTIMIZED_FUNCTIONS_LIST, head); + } + return result_list_head; +} + + +void Deoptimizer::ReplaceCodeForRelatedFunctions(JSFunction* function, + Code* code) { + Context* context = function->context()->native_context(); + + SharedFunctionInfo* shared = function->shared(); + + Object* undefined = Isolate::Current()->heap()->undefined_value(); + Object* current = CutOutRelatedFunctionsList(context, code, undefined); + + while (current != undefined) { + JSFunction* func = JSFunction::cast(current); + current = func->next_function_link(); + func->set_code(shared->code()); + func->set_next_function_link(undefined); + } +} + + FrameDescription::FrameDescription(uint32_t frame_size, JSFunction* function) : frame_size_(frame_size), @@ -1290,7 +1604,7 @@ Object* FrameDescription::GetExpression(int index) { } -void TranslationBuffer::Add(int32_t value) { +void TranslationBuffer::Add(int32_t value, Zone* zone) { // Encode the sign bit in the least significant bit. bool is_negative = (value < 0); uint32_t bits = ((is_negative ? -value : value) << 1) | @@ -1299,7 +1613,7 @@ void TranslationBuffer::Add(int32_t value) { // each byte to indicate whether or not more bytes follow. do { uint32_t next = bits >> 7; - contents_.Add(((bits << 1) & 0xFF) | (next != 0)); + contents_.Add(((bits << 1) & 0xFF) | (next != 0), zone); bits = next; } while (bits != 0); } @@ -1332,95 +1646,127 @@ Handle<ByteArray> TranslationBuffer::CreateByteArray() { void Translation::BeginConstructStubFrame(int literal_id, unsigned height) { - buffer_->Add(CONSTRUCT_STUB_FRAME); - buffer_->Add(literal_id); - buffer_->Add(height); + buffer_->Add(CONSTRUCT_STUB_FRAME, zone()); + buffer_->Add(literal_id, zone()); + buffer_->Add(height, zone()); +} + + +void Translation::BeginGetterStubFrame(int literal_id) { + buffer_->Add(GETTER_STUB_FRAME, zone()); + buffer_->Add(literal_id, zone()); +} + + +void Translation::BeginSetterStubFrame(int literal_id) { + buffer_->Add(SETTER_STUB_FRAME, zone()); + buffer_->Add(literal_id, zone()); } void Translation::BeginArgumentsAdaptorFrame(int literal_id, unsigned height) { - buffer_->Add(ARGUMENTS_ADAPTOR_FRAME); - buffer_->Add(literal_id); - buffer_->Add(height); + buffer_->Add(ARGUMENTS_ADAPTOR_FRAME, zone()); + buffer_->Add(literal_id, zone()); + buffer_->Add(height, zone()); } -void Translation::BeginJSFrame(int node_id, int literal_id, unsigned height) { - buffer_->Add(JS_FRAME); - buffer_->Add(node_id); - buffer_->Add(literal_id); - buffer_->Add(height); +void Translation::BeginJSFrame(BailoutId node_id, + int literal_id, + unsigned height) { + buffer_->Add(JS_FRAME, zone()); + buffer_->Add(node_id.ToInt(), zone()); + buffer_->Add(literal_id, zone()); + buffer_->Add(height, zone()); } void Translation::StoreRegister(Register reg) { - buffer_->Add(REGISTER); - buffer_->Add(reg.code()); + buffer_->Add(REGISTER, zone()); + buffer_->Add(reg.code(), zone()); } void Translation::StoreInt32Register(Register reg) { - buffer_->Add(INT32_REGISTER); - buffer_->Add(reg.code()); + buffer_->Add(INT32_REGISTER, zone()); + buffer_->Add(reg.code(), zone()); +} + + +void Translation::StoreUint32Register(Register reg) { + buffer_->Add(UINT32_REGISTER, zone()); + buffer_->Add(reg.code(), zone()); } void Translation::StoreDoubleRegister(DoubleRegister reg) { - buffer_->Add(DOUBLE_REGISTER); - buffer_->Add(DoubleRegister::ToAllocationIndex(reg)); + buffer_->Add(DOUBLE_REGISTER, zone()); + buffer_->Add(DoubleRegister::ToAllocationIndex(reg), zone()); } void Translation::StoreStackSlot(int index) { - buffer_->Add(STACK_SLOT); - buffer_->Add(index); + buffer_->Add(STACK_SLOT, zone()); + buffer_->Add(index, zone()); } void Translation::StoreInt32StackSlot(int index) { - buffer_->Add(INT32_STACK_SLOT); - buffer_->Add(index); + buffer_->Add(INT32_STACK_SLOT, zone()); + buffer_->Add(index, zone()); +} + + +void Translation::StoreUint32StackSlot(int index) { + buffer_->Add(UINT32_STACK_SLOT, zone()); + buffer_->Add(index, zone()); } void Translation::StoreDoubleStackSlot(int index) { - buffer_->Add(DOUBLE_STACK_SLOT); - buffer_->Add(index); + buffer_->Add(DOUBLE_STACK_SLOT, zone()); + buffer_->Add(index, zone()); } void Translation::StoreLiteral(int literal_id) { - buffer_->Add(LITERAL); - buffer_->Add(literal_id); + buffer_->Add(LITERAL, zone()); + buffer_->Add(literal_id, zone()); } -void Translation::StoreArgumentsObject() { - buffer_->Add(ARGUMENTS_OBJECT); +void Translation::StoreArgumentsObject(int args_index, int args_length) { + buffer_->Add(ARGUMENTS_OBJECT, zone()); + buffer_->Add(args_index, zone()); + buffer_->Add(args_length, zone()); } void Translation::MarkDuplicate() { - buffer_->Add(DUPLICATE); + buffer_->Add(DUPLICATE, zone()); } int Translation::NumberOfOperandsFor(Opcode opcode) { switch (opcode) { - case ARGUMENTS_OBJECT: case DUPLICATE: return 0; + case GETTER_STUB_FRAME: + case SETTER_STUB_FRAME: case REGISTER: case INT32_REGISTER: + case UINT32_REGISTER: case DOUBLE_REGISTER: case STACK_SLOT: case INT32_STACK_SLOT: + case UINT32_STACK_SLOT: case DOUBLE_STACK_SLOT: case LITERAL: return 1; case BEGIN: case ARGUMENTS_ADAPTOR_FRAME: case CONSTRUCT_STUB_FRAME: + case ARGUMENTS_OBJECT: return 2; case JS_FRAME: return 3; @@ -1442,16 +1788,24 @@ const char* Translation::StringFor(Opcode opcode) { return "ARGUMENTS_ADAPTOR_FRAME"; case CONSTRUCT_STUB_FRAME: return "CONSTRUCT_STUB_FRAME"; + case GETTER_STUB_FRAME: + return "GETTER_STUB_FRAME"; + case SETTER_STUB_FRAME: + return "SETTER_STUB_FRAME"; case REGISTER: return "REGISTER"; case INT32_REGISTER: return "INT32_REGISTER"; + case UINT32_REGISTER: + return "UINT32_REGISTER"; case DOUBLE_REGISTER: return "DOUBLE_REGISTER"; case STACK_SLOT: return "STACK_SLOT"; case INT32_STACK_SLOT: return "INT32_STACK_SLOT"; + case UINT32_STACK_SLOT: + return "UINT32_STACK_SLOT"; case DOUBLE_STACK_SLOT: return "DOUBLE_STACK_SLOT"; case LITERAL: @@ -1498,6 +1852,8 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, case Translation::JS_FRAME: case Translation::ARGUMENTS_ADAPTOR_FRAME: case Translation::CONSTRUCT_STUB_FRAME: + case Translation::GETTER_STUB_FRAME: + case Translation::SETTER_STUB_FRAME: // Peeled off before getting here. break; @@ -1507,6 +1863,7 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, case Translation::REGISTER: case Translation::INT32_REGISTER: + case Translation::UINT32_REGISTER: case Translation::DOUBLE_REGISTER: case Translation::DUPLICATE: // We are at safepoint which corresponds to call. All registers are @@ -1526,6 +1883,12 @@ SlotRef SlotRef::ComputeSlotForNextArgument(TranslationIterator* iterator, return SlotRef(slot_addr, SlotRef::INT32); } + case Translation::UINT32_STACK_SLOT: { + int slot_index = iterator->Next(); + Address slot_addr = SlotAddress(frame, slot_index); + return SlotRef(slot_addr, SlotRef::UINT32); + } + case Translation::DOUBLE_STACK_SLOT: { int slot_index = iterator->Next(); Address slot_addr = SlotAddress(frame, slot_index); @@ -1565,7 +1928,7 @@ Vector<SlotRef> SlotRef::ComputeSlotMappingForArguments( int inlined_jsframe_index, int formal_parameter_count) { AssertNoAllocation no_gc; - int deopt_index = AstNode::kNoNumber; + int deopt_index = Safepoint::kNoDeoptimizationIndex; DeoptimizationInputData* data = static_cast<OptimizedFrame*>(frame)->GetDeoptimizationData(&deopt_index); TranslationIterator it(data->TranslationByteArray(), |