Chromium Code Reviews| Index: src/deoptimizer.cc |
| diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc |
| index 23f26c724ab4d69931bfe0c0eedf73ba4e7adf79..7f43e34f5a4c4b0b246aa7c2e35e02a9061faa97 100644 |
| --- a/src/deoptimizer.cc |
| +++ b/src/deoptimizer.cc |
| @@ -763,6 +763,10 @@ void Deoptimizer::DoComputeOutputFrames() { |
| DoComputeJSFrame(frame_index); |
| jsframe_count_++; |
| break; |
| + case TranslatedFrame::kInterpretedFunction: |
| + DoComputeInterpretedFrame(frame_index); |
| + jsframe_count_++; |
| + break; |
| case TranslatedFrame::kArgumentsAdaptor: |
| DoComputeArgumentsAdaptorFrame(frame_index); |
| break; |
| @@ -828,7 +832,8 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) { |
| // The 'fixed' part of the frame consists of the incoming parameters and |
| // the part described by JavaScriptFrameConstants. |
| - unsigned fixed_frame_size = ComputeFixedSize(function); |
| + unsigned fixed_frame_size = |
| + ComputeFixedSize(function, StackFrame::JAVA_SCRIPT); |
| unsigned input_frame_size = input_->GetFrameSize(); |
| unsigned output_frame_size = height_in_bytes + fixed_frame_size; |
| @@ -1022,6 +1027,239 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) { |
| } |
| +void Deoptimizer::DoComputeInterpretedFrame(int frame_index) { |
| + TranslatedFrame* translated_frame = |
| + &(translated_state_.frames()[frame_index]); |
| + TranslatedFrame::iterator value_iterator = translated_frame->begin(); |
| + int input_index = 0; |
| + |
| + BailoutId bytecode_offset = translated_frame->node_id(); |
| + unsigned height = translated_frame->height(); |
| + unsigned height_in_bytes = height * kPointerSize; |
| + JSFunction* function = JSFunction::cast(value_iterator->GetRawValue()); |
| + value_iterator++; |
| + input_index++; |
| + if (trace_scope_ != NULL) { |
| + PrintF(trace_scope_->file(), " translating interpreted frame "); |
| + function->PrintName(trace_scope_->file()); |
| + PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d\n", |
| + bytecode_offset.ToInt(), height_in_bytes); |
| + } |
| + |
| + // The 'fixed' part of the frame consists of the incoming parameters and |
| + // the part described by JavaScriptFrameConstants. |
| + unsigned fixed_frame_size = |
| + ComputeFixedSize(function, StackFrame::INTERPRETED); |
| + unsigned input_frame_size = input_->GetFrameSize(); |
| + unsigned output_frame_size = height_in_bytes + fixed_frame_size; |
| + |
| + // Allocate and store the output frame description. |
| + FrameDescription* output_frame = |
| + new (output_frame_size) FrameDescription(output_frame_size, function); |
| + output_frame->SetFrameType(StackFrame::INTERPRETED); |
| + |
| + bool is_bottommost = (0 == frame_index); |
| + bool is_topmost = (output_count_ - 1 == frame_index); |
| + CHECK(frame_index >= 0 && frame_index < output_count_); |
| + CHECK_NULL(output_[frame_index]); |
| + output_[frame_index] = output_frame; |
| + |
| + // The top address for the bottommost output frame can be computed from |
| + // the input frame pointer and the output frame's height. For all |
| + // subsequent output frames, it can be computed from the previous one's |
| + // top address and the current frame's size. |
| + Register fp_reg = InterpretedFrame::fp_register(); |
| + intptr_t top_address; |
| + if (is_bottommost) { |
| + // Determine whether the input frame contains alignment padding. |
| + has_alignment_padding_ = |
| + (!compiled_code_->is_turbofanned() && HasAlignmentPadding(function)) |
| + ? 1 |
| + : 0; |
| + // 2 = context and function in the frame. |
| + // If the optimized frame had alignment padding, adjust the frame pointer |
| + // to point to the new position of the old frame pointer after padding |
| + // is removed. Subtract 2 * kPointerSize for the context and function slots. |
| + top_address = input_->GetRegister(fp_reg.code()) - |
| + InterpreterFrameConstants::kFixedFrameSizeFromFp - |
| + height_in_bytes + has_alignment_padding_ * kPointerSize; |
| + } else { |
| + top_address = output_[frame_index - 1]->GetTop() - output_frame_size; |
| + } |
| + output_frame->SetTop(top_address); |
| + |
| + // Compute the incoming parameter translation. |
| + int parameter_count = |
| + function->shared()->internal_formal_parameter_count() + 1; |
| + unsigned output_offset = output_frame_size; |
| + unsigned input_offset = input_frame_size; |
| + for (int i = 0; i < parameter_count; ++i) { |
| + output_offset -= kPointerSize; |
| + WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index, |
| + output_offset); |
| + } |
| + input_offset -= (parameter_count * kPointerSize); |
| + |
| + // There are no translation commands for the caller's pc and fp, the |
| + // context, the function, new.target and the bytecode offset. Synthesize |
| + // their values and set them up |
| + // explicitly. |
| + // |
| + // The caller's pc for the bottommost output frame is the same as in the |
| + // input frame. For all subsequent output frames, it can be read from the |
| + // previous one. This frame's pc can be computed from the non-optimized |
| + // function code and AST id of the bailout. |
| + output_offset -= kPCOnStackSize; |
| + input_offset -= kPCOnStackSize; |
| + intptr_t value; |
| + if (is_bottommost) { |
| + value = input_->GetFrameSlot(input_offset); |
| + } else { |
| + value = output_[frame_index - 1]->GetPc(); |
| + } |
| + output_frame->SetCallerPc(output_offset, value); |
| + DebugPrintOutputSlot(value, frame_index, output_offset, "caller's pc\n"); |
| + |
| + // The caller's frame pointer for the bottommost output frame is the same |
| + // as in the input frame. For all subsequent output frames, it can be |
| + // read from the previous one. Also compute and set this frame's frame |
| + // pointer. |
| + output_offset -= kFPOnStackSize; |
| + input_offset -= kFPOnStackSize; |
| + if (is_bottommost) { |
| + value = input_->GetFrameSlot(input_offset); |
| + } else { |
| + value = output_[frame_index - 1]->GetFp(); |
| + } |
| + output_frame->SetCallerFp(output_offset, value); |
| + intptr_t fp_value = top_address + output_offset; |
| + DCHECK(!is_bottommost || |
| + (input_->GetRegister(fp_reg.code()) + |
| + has_alignment_padding_ * kPointerSize) == fp_value); |
| + output_frame->SetFp(fp_value); |
| + if (is_topmost) output_frame->SetRegister(fp_reg.code(), fp_value); |
| + DebugPrintOutputSlot(value, frame_index, output_offset, "caller's fp\n"); |
| + DCHECK(!is_bottommost || !has_alignment_padding_ || |
| + (fp_value & kPointerSize) != 0); |
| + |
| + if (FLAG_enable_embedded_constant_pool) { |
| + // For the bottommost output frame the constant pool pointer can be gotten |
| + // from the input frame. For subsequent output frames, it can be read from |
| + // the previous frame. |
| + output_offset -= kPointerSize; |
| + input_offset -= kPointerSize; |
| + if (is_bottommost) { |
| + value = input_->GetFrameSlot(input_offset); |
| + } else { |
| + value = output_[frame_index - 1]->GetConstantPool(); |
| + } |
| + output_frame->SetCallerConstantPool(output_offset, value); |
| + DebugPrintOutputSlot(value, frame_index, output_offset, |
| + "caller's constant_pool\n"); |
| + } |
| + |
| + // For the bottommost output frame the context can be gotten from the input |
| + // frame. For all subsequent output frames it can be gotten from the function |
| + // so long as we don't inline functions that need local contexts. |
| + Register context_reg = InterpretedFrame::context_register(); |
| + output_offset -= kPointerSize; |
| + input_offset -= kPointerSize; |
| + // Read the context from the translations. |
| + Object* context = value_iterator->GetRawValue(); |
| + // The context should not be a placeholder for a materialized object. |
| + CHECK(context != isolate_->heap()->arguments_marker()); |
| + if (context == isolate_->heap()->undefined_value()) { |
|
Jarin
2015/12/17 13:35:09
You should not need this case, that is only for cr
rmcilroy
2015/12/17 23:49:54
Good point, removed! Also removed the "!compiled_c
|
| + // If the context was optimized away, just use the context from |
| + // the activation. This should only apply to Crankshaft code. |
| + CHECK(!compiled_code_->is_turbofanned()); |
| + context = |
| + is_bottommost |
| + ? reinterpret_cast<Object*>(input_->GetFrameSlot(input_offset)) |
| + : function->context(); |
| + } |
| + value = reinterpret_cast<intptr_t>(context); |
| + output_frame->SetContext(value); |
| + if (is_topmost) output_frame->SetRegister(context_reg.code(), value); |
| + WriteValueToOutput(context, input_index, frame_index, output_offset, |
| + "context "); |
| + value_iterator++; |
| + input_index++; |
| + |
| + // The function was mentioned explicitly in the BEGIN_FRAME. |
| + output_offset -= kPointerSize; |
| + input_offset -= kPointerSize; |
| + value = reinterpret_cast<intptr_t>(function); |
| + // The function for the bottommost output frame should also agree with the |
| + // input frame. |
| + DCHECK(!is_bottommost || input_->GetFrameSlot(input_offset) == value); |
| + WriteValueToOutput(function, 0, frame_index, output_offset, "function "); |
| + |
| + // TODO(rmcilroy): Deal with new.target correctly - currently just set it to |
| + // undefined. |
| + output_offset -= kPointerSize; |
| + input_offset -= kPointerSize; |
| + Object* new_target = isolate_->heap()->undefined_value(); |
| + WriteValueToOutput(new_target, 0, frame_index, output_offset, "new_target "); |
| + |
| + // The bytecode offset was mentioned explicitly in the BEGIN_FRAME. |
| + output_offset -= kPointerSize; |
| + input_offset -= kPointerSize; |
| + int raw_bytecode_offset = |
| + BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset.ToInt(); |
| + Smi* smi_bytecode_offset = Smi::FromInt(raw_bytecode_offset); |
| + WriteValueToOutput(smi_bytecode_offset, 0, frame_index, output_offset, |
| + "bytecode offset "); |
| + |
| + // Translate the rest of the interpreter registers in the frame. |
| + for (unsigned i = 0; i < height; ++i) { |
| + output_offset -= kPointerSize; |
| + WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index, |
| + output_offset); |
| + } |
| + CHECK_EQ(0u, output_offset); |
| + |
| + // Set the accumulator register. |
| + output_frame->SetRegister( |
| + kInterpreterAccumulatorRegister.code(), |
| + reinterpret_cast<intptr_t>(value_iterator->GetRawValue())); |
| + value_iterator++; |
| + |
| + Builtins* builtins = isolate_->builtins(); |
| + Code* trampoline = builtins->builtin(Builtins::kInterpreterEntryTrampoline); |
| + output_frame->SetPc(reinterpret_cast<intptr_t>(trampoline->entry())); |
| + output_frame->SetState(0); |
| + |
| + // Update constant pool. |
| + if (FLAG_enable_embedded_constant_pool) { |
| + intptr_t constant_pool_value = |
| + reinterpret_cast<intptr_t>(trampoline->constant_pool()); |
| + output_frame->SetConstantPool(constant_pool_value); |
| + if (is_topmost) { |
| + Register constant_pool_reg = |
| + InterpretedFrame::constant_pool_pointer_register(); |
| + output_frame->SetRegister(constant_pool_reg.code(), constant_pool_value); |
| + } |
| + } |
| + |
| + // Set the continuation for the topmost frame. |
| + if (is_topmost && bailout_type_ != DEBUGGER) { |
| + Code* continuation = |
| + builtins->builtin(Builtins::kInterpreterNotifyDeoptimized); |
| + if (bailout_type_ == LAZY) { |
| + continuation = |
| + builtins->builtin(Builtins::kInterpreterNotifyLazyDeoptimized); |
| + } else if (bailout_type_ == SOFT) { |
| + continuation = |
| + builtins->builtin(Builtins::kInterpreterNotifySoftDeoptimized); |
| + } else { |
| + CHECK_EQ(bailout_type_, EAGER); |
| + } |
| + output_frame->SetContinuation( |
| + reinterpret_cast<intptr_t>(continuation->entry())); |
| + } |
| +} |
| + |
| + |
| void Deoptimizer::DoComputeArgumentsAdaptorFrame(int frame_index) { |
| TranslatedFrame* translated_frame = |
| &(translated_state_.frames()[frame_index]); |
| @@ -1756,7 +1994,10 @@ void Deoptimizer::DebugPrintOutputSlot(intptr_t value, int frame_index, |
| unsigned Deoptimizer::ComputeInputFrameSize() const { |
| - unsigned fixed_size = ComputeFixedSize(function_); |
| + StackFrame::Type type = compiled_code_->kind() == Code::OPTIMIZED_FUNCTION |
| + ? StackFrame::OPTIMIZED |
| + : StackFrame::JAVA_SCRIPT; |
| + unsigned fixed_size = ComputeFixedSize(function_, type); |
|
Jarin
2015/12/17 13:35:09
I am a bit confused here. Input frame should be al
rmcilroy
2015/12/17 23:49:54
I agree, I always see OPTIMZED here, I was just co
Jarin
2015/12/18 08:58:21
I am not completely sure, but I think it is there
rmcilroy
2015/12/18 15:18:10
Ahh that makes sense. I didn't think they could de
|
| // The fp-to-sp delta already takes the context, constant pool pointer and the |
| // function into account so we have to avoid double counting them. |
| unsigned result = fixed_size + fp_to_sp_delta_ - |
| @@ -1771,11 +2012,14 @@ unsigned Deoptimizer::ComputeInputFrameSize() const { |
| } |
| -unsigned Deoptimizer::ComputeFixedSize(JSFunction* function) const { |
| +unsigned Deoptimizer::ComputeFixedSize(JSFunction* function, |
| + StackFrame::Type type) const { |
|
Jarin
2015/12/17 13:35:09
As far as I can see all callers statically know wh
rmcilroy
2015/12/17 23:49:54
Done.
|
| // The fixed part of the frame consists of the return address, frame |
| // pointer, function, context, and all the incoming arguments. |
| return ComputeIncomingArgumentSize(function) + |
| - StandardFrameConstants::kFixedFrameSize; |
| + (type == StackFrame::INTERPRETED |
| + ? InterpreterFrameConstants::kFixedFrameSize |
| + : StandardFrameConstants::kFixedFrameSize); |
| } |
| @@ -1872,8 +2116,13 @@ FrameDescription::FrameDescription(uint32_t frame_size, |
| int FrameDescription::ComputeFixedSize() { |
| - return StandardFrameConstants::kFixedFrameSize + |
| - (ComputeParametersCount() + 1) * kPointerSize; |
| + if (type_ == StackFrame::INTERPRETED) { |
| + return InterpreterFrameConstants::kFixedFrameSize + |
| + (ComputeParametersCount() + 1) * kPointerSize; |
| + } else { |
| + return StandardFrameConstants::kFixedFrameSize + |
| + (ComputeParametersCount() + 1) * kPointerSize; |
| + } |
| } |
| @@ -2011,6 +2260,15 @@ void Translation::BeginJSFrame(BailoutId node_id, |
| } |
| +void Translation::BeginInterpretedFrame(BailoutId bytecode_offset, |
| + int literal_id, unsigned height) { |
| + buffer_->Add(INTERPRETED_FRAME, zone()); |
| + buffer_->Add(bytecode_offset.ToInt(), zone()); |
| + buffer_->Add(literal_id, zone()); |
| + buffer_->Add(height, zone()); |
| +} |
| + |
| + |
| void Translation::BeginCompiledStubFrame(int height) { |
| buffer_->Add(COMPILED_STUB_FRAME, zone()); |
| buffer_->Add(height, zone()); |
| @@ -2143,6 +2401,7 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { |
| case CONSTRUCT_STUB_FRAME: |
| return 2; |
| case JS_FRAME: |
| + case INTERPRETED_FRAME: |
| return 3; |
| } |
| FATAL("Unexpected translation type"); |
| @@ -2616,6 +2875,15 @@ TranslatedFrame TranslatedFrame::JSFrame(BailoutId node_id, |
| } |
| +TranslatedFrame TranslatedFrame::InterpretedFrame( |
| + BailoutId bytecode_offset, SharedFunctionInfo* shared_info, int height) { |
| + TranslatedFrame frame(kInterpretedFunction, shared_info->GetIsolate(), |
| + shared_info, height); |
| + frame.node_id_ = bytecode_offset; |
| + return frame; |
| +} |
| + |
| + |
| TranslatedFrame TranslatedFrame::AccessorFrame( |
| Kind kind, SharedFunctionInfo* shared_info) { |
| DCHECK(kind == kSetter || kind == kGetter); |
| @@ -2645,6 +2913,12 @@ int TranslatedFrame::GetValueCount() { |
| return height_ + parameter_count + 1; |
| } |
| + case kInterpretedFunction: { |
| + int parameter_count = |
| + raw_shared_info_->internal_formal_parameter_count() + 1; |
| + return height_ + parameter_count + 3; |
|
Jarin
2015/12/17 13:35:09
Could you possibly add a comment, which explains w
rmcilroy
2015/12/17 23:49:54
Done. Also for kFunction above.
|
| + } |
| + |
| case kGetter: |
| return 2; // Function and receiver. |
| @@ -2700,6 +2974,24 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame( |
| return TranslatedFrame::JSFrame(node_id, shared_info, height); |
| } |
| + case Translation::INTERPRETED_FRAME: { |
| + BailoutId bytecode_offset = BailoutId(iterator->Next()); |
| + SharedFunctionInfo* shared_info = |
| + SharedFunctionInfo::cast(literal_array->get(iterator->Next())); |
| + int height = iterator->Next(); |
| + if (trace_file != nullptr) { |
| + base::SmartArrayPointer<char> name = |
| + shared_info->DebugName()->ToCString(); |
| + PrintF(trace_file, " reading input frame %s", name.get()); |
| + int arg_count = shared_info->internal_formal_parameter_count() + 1; |
| + PrintF(trace_file, |
| + " => bytecode_offset=%d, args=%d, height=%d; inputs:\n", |
| + bytecode_offset.ToInt(), arg_count, height); |
| + } |
| + return TranslatedFrame::InterpretedFrame(bytecode_offset, shared_info, |
| + height); |
| + } |
| + |
| case Translation::ARGUMENTS_ADAPTOR_FRAME: { |
| SharedFunctionInfo* shared_info = |
| SharedFunctionInfo::cast(literal_array->get(iterator->Next())); |
| @@ -2812,6 +3104,7 @@ TranslatedValue TranslatedState::CreateNextTranslatedValue( |
| switch (opcode) { |
| case Translation::BEGIN: |
| case Translation::JS_FRAME: |
| + case Translation::INTERPRETED_FRAME: |
| case Translation::ARGUMENTS_ADAPTOR_FRAME: |
| case Translation::CONSTRUCT_STUB_FRAME: |
| case Translation::GETTER_STUB_FRAME: |