Index: src/deoptimizer.cc |
diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc |
index 6ad0b76c07f39abadc7a9fd41f684b008a836196..f5c56243a0fabda2932411d22bb12735241b8695 100644 |
--- a/src/deoptimizer.cc |
+++ b/src/deoptimizer.cc |
@@ -827,6 +827,12 @@ void Deoptimizer::DoComputeOutputFrames() { |
case TranslatedFrame::kCompiledStub: |
DoComputeCompiledStubFrame(translated_frame, frame_index); |
break; |
+ case TranslatedFrame::kBuiltinContinuation: |
+ DoComputeBuiltinContinuation(translated_frame, frame_index, false); |
+ break; |
+ case TranslatedFrame::kJavaScriptBuiltinContinuation: |
+ DoComputeBuiltinContinuation(translated_frame, frame_index, true); |
+ break; |
case TranslatedFrame::kInvalid: |
FATAL("invalid frame"); |
break; |
@@ -2161,6 +2167,292 @@ void Deoptimizer::DoComputeCompiledStubFrame(TranslatedFrame* translated_frame, |
reinterpret_cast<intptr_t>(notify_failure->entry())); |
} |
+// BuiltinContinuationFrames capture the machine state that is expected as input |
+// to a builtin, including both input register values and stack parameters. When |
+// the frame is reactivated (i.e. the frame below it returns), a |
+// ContinueToBuiltin stub restores the register state from the frame and tail |
+// calls to the actual target builtin, making it appear that the stub had been |
+// directly called by the frame above it. The input values to populate the frame |
+// are taken from the deopt's FrameState. |
+// |
+// Frame translation happens in two modes, EAGER and LAZY. In EAGER mode, all of |
+// the parameters to the Builtin are explicitly specified in the TurboFan |
+// FrameState node. In LAZY mode, there is always one fewer parameters specified |
+// in the FrameState than expected by the Builtin. In that case, construction of |
+// BuiltinContinuationFrame adds the final missing parameter during |
+// deoptimization, and that parameter is always on the stack and contains the |
+// value returned from the callee of the call site triggering the LAZY deopt |
+// (e.g. rax on x64). This requires that continuation Builtins for LAZY deopts |
+// must have at least one stack parameter. |
+// |
+// TO |
+// | .... | |
+// +-------------------------+ |
+// | builtin param 0 |<- FrameState input value n becomes |
+// +-------------------------+ |
+// | ... | |
+// +-------------------------+ |
+// | builtin param m |<- FrameState input value n+m-1, or in |
+// +-------------------------+ the LAZY case, return LAZY result value |
+// | ContinueToBuiltin entry | |
+// +-------------------------+ |
+// | | saved frame (FP) | |
+// | +=========================+<- fpreg |
+// | |constant pool (if ool_cp)| |
+// v +-------------------------+ |
+// |BUILTIN_CONTINUATION mark| |
+// +-------------------------+ |
+// | JS Builtin code object | |
+// +-------------------------+ |
+// | builtin input GPR reg0 |<- populated from deopt FrameState using |
+// +-------------------------+ the builtin's CallInterfaceDescriptor |
+// | ... | to map a FrameState's 0..n-1 inputs to |
+// +-------------------------+ the builtin's n input register params. |
+// | builtin input GPR regn | |
+// |-------------------------|<- spreg |
+// |
+void Deoptimizer::DoComputeBuiltinContinuation( |
+ TranslatedFrame* translated_frame, int frame_index, |
+ bool java_script_builtin) { |
+ TranslatedFrame::iterator value_iterator = translated_frame->begin(); |
+ int input_index = 0; |
+ |
+ // The output frame must have room for all of the parameters that need to be |
+ // passed to the builtin continuation. |
+ int height_in_words = translated_frame->height(); |
+ |
+ BailoutId bailout_id = translated_frame->node_id(); |
+ Builtins::Name builtin_name = Builtins::GetBuiltinFromBailoutId(bailout_id); |
+ Code* builtin = isolate()->builtins()->builtin(builtin_name); |
+ Callable continuation_callable = |
+ Builtins::CallableFor(isolate(), builtin_name); |
+ CallInterfaceDescriptor continuation_descriptor = |
+ continuation_callable.descriptor(); |
+ |
+ bool is_bottommost = (0 == frame_index); |
+ bool is_topmost = (output_count_ - 1 == frame_index); |
+ bool must_handle_result = !is_topmost || bailout_type_ == LAZY; |
+ |
+ const RegisterConfiguration* config(RegisterConfiguration::Turbofan()); |
+ int allocatable_register_count = config->num_allocatable_general_registers(); |
+ int register_parameter_count = |
+ continuation_descriptor.GetRegisterParameterCount(); |
+ // Make sure to account for the context by removing it from the register |
+ // parameter count. |
+ int stack_param_count = height_in_words - register_parameter_count - 1; |
+ if (must_handle_result) stack_param_count++; |
+ int output_frame_size = |
+ kPointerSize * (stack_param_count + allocatable_register_count) + |
+ TYPED_FRAME_SIZE(2); // For destination builtin code and registers |
+ |
+ // Validate types of parameters. They must all be tagged except for argc for |
+ // JS builtins. |
+ bool has_argc = false; |
+ for (int i = 0; i < register_parameter_count; ++i) { |
+ MachineType type = continuation_descriptor.GetParameterType(i); |
+ int code = continuation_descriptor.GetRegisterParameter(i).code(); |
+ // Only tagged and int32 arguments are supported, and int32 only for the |
+ // arguments count on JavaScript builtins. |
+ if (type == MachineType::Int32()) { |
+ CHECK_EQ(code, kJavaScriptCallArgCountRegister.code()); |
+ has_argc = true; |
+ } else { |
+ // Any other argument must be a tagged value. |
+ CHECK(IsAnyTagged(type.representation())); |
+ } |
+ } |
+ CHECK_EQ(java_script_builtin, has_argc); |
+ |
+ if (trace_scope_ != NULL) { |
+ PrintF(trace_scope_->file(), |
+ " translating BuiltinContinuation to %s, stack param count %d\n", |
+ Builtins::name(builtin_name), stack_param_count); |
+ } |
+ |
+ unsigned output_frame_offset = output_frame_size; |
+ FrameDescription* output_frame = |
+ new (output_frame_size) FrameDescription(output_frame_size); |
+ output_[frame_index] = output_frame; |
+ |
+ // The top address of the frame is computed from the previous frame's top and |
+ // this frame's size. |
+ intptr_t top_address; |
+ if (is_bottommost) { |
+ top_address = caller_frame_top_ - output_frame_size; |
+ } else { |
+ top_address = output_[frame_index - 1]->GetTop() - output_frame_size; |
+ } |
+ output_frame->SetTop(top_address); |
+ |
+ output_frame->SetState( |
+ Smi::FromInt(static_cast<int>(BailoutState::NO_REGISTERS))); |
+ |
+ // Get the possible JSFunction for the case that |
+ intptr_t maybe_function = |
+ reinterpret_cast<intptr_t>(value_iterator->GetRawValue()); |
+ ++value_iterator; |
+ |
+ std::vector<intptr_t> register_values; |
+ int total_registers = config->num_general_registers(); |
+ register_values.resize(total_registers, 0); |
+ for (int i = 0; i < total_registers; ++i) { |
+ register_values[i] = 0; |
+ } |
+ |
+ intptr_t value; |
+ |
+ Register result_reg = FullCodeGenerator::result_register(); |
+ if (must_handle_result) { |
+ value = input_->GetRegister(result_reg.code()); |
+ } else { |
+ value = reinterpret_cast<intptr_t>(isolate()->heap()->undefined_value()); |
+ } |
+ output_frame->SetRegister(result_reg.code(), value); |
+ |
+ int translated_stack_parameters = |
+ must_handle_result ? stack_param_count - 1 : stack_param_count; |
+ |
+ for (int i = 0; i < translated_stack_parameters; ++i) { |
+ output_frame_offset -= kPointerSize; |
+ WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index, |
+ output_frame_offset); |
+ } |
+ |
+ if (must_handle_result) { |
+ output_frame_offset -= kPointerSize; |
+ WriteValueToOutput(isolate()->heap()->the_hole_value(), input_index, |
+ frame_index, output_frame_offset, |
+ "placeholder for return result on lazy deopt "); |
+ } |
+ |
+ for (int i = 0; i < register_parameter_count; ++i) { |
+ value = reinterpret_cast<intptr_t>(value_iterator->GetRawValue()); |
+ int code = continuation_descriptor.GetRegisterParameter(i).code(); |
+ register_values[code] = value; |
+ ++input_index; |
+ ++value_iterator; |
+ } |
+ |
+ // The context register is always implicit in the CallInterfaceDescriptor but |
+ // its register must be explicitly set when continuing to the builtin. Make |
+ // sure that it's harvested from the translation and copied into the register |
+ // set (it was automatically added at the end of the FrameState by the |
+ // instruction selector). |
+ value = reinterpret_cast<intptr_t>(value_iterator->GetRawValue()); |
+ register_values[kContextRegister.code()] = value; |
+ output_frame->SetContext(value); |
+ output_frame->SetRegister(kContextRegister.code(), value); |
+ ++input_index; |
+ ++value_iterator; |
+ |
+ // Set caller's PC (JSFunction continuation). |
+ output_frame_offset -= kPCOnStackSize; |
+ if (is_bottommost) { |
+ value = caller_pc_; |
+ } else { |
+ value = output_[frame_index - 1]->GetPc(); |
+ } |
+ output_frame->SetCallerPc(output_frame_offset, value); |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ "caller's pc\n"); |
+ |
+ // Read caller's FP from the previous frame, and set this frame's FP. |
+ output_frame_offset -= kFPOnStackSize; |
+ if (is_bottommost) { |
+ value = caller_fp_; |
+ } else { |
+ value = output_[frame_index - 1]->GetFp(); |
+ } |
+ output_frame->SetCallerFp(output_frame_offset, value); |
+ intptr_t fp_value = top_address + output_frame_offset; |
+ output_frame->SetFp(fp_value); |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ "caller's fp\n"); |
+ |
+ if (FLAG_enable_embedded_constant_pool) { |
+ // Read the caller's constant pool from the previous frame. |
+ output_frame_offset -= kPointerSize; |
+ if (is_bottommost) { |
+ value = caller_constant_pool_; |
+ } else { |
+ value = output_[frame_index - 1]->GetConstantPool(); |
+ } |
+ output_frame->SetCallerConstantPool(output_frame_offset, value); |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ "caller's constant_pool\n"); |
+ } |
+ |
+ // A marker value is used in place of the context. |
+ output_frame_offset -= kPointerSize; |
+ intptr_t marker = |
+ java_script_builtin |
+ ? StackFrame::TypeToMarker( |
+ StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION) |
+ : StackFrame::TypeToMarker(StackFrame::BUILTIN_CONTINUATION); |
+ output_frame->SetFrameSlot(output_frame_offset, marker); |
+ DebugPrintOutputSlot(marker, frame_index, output_frame_offset, |
+ "context (builtin continuation sentinel)\n"); |
+ |
+ output_frame_offset -= kPointerSize; |
+ value = java_script_builtin ? maybe_function : 0; |
+ output_frame->SetFrameSlot(output_frame_offset, value); |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ java_script_builtin ? "JSFunction\n" : "unused\n"); |
+ |
+ // The builtin to continue to |
+ output_frame_offset -= kPointerSize; |
+ value = reinterpret_cast<intptr_t>(builtin); |
+ output_frame->SetFrameSlot(output_frame_offset, value); |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ "builtin address\n"); |
+ |
+ for (int i = 0; i < allocatable_register_count; ++i) { |
+ output_frame_offset -= kPointerSize; |
+ int code = config->GetAllocatableGeneralCode(i); |
+ value = register_values[code]; |
+ output_frame->SetFrameSlot(output_frame_offset, value); |
+ if (trace_scope_ != nullptr) { |
+ ScopedVector<char> str(128); |
+ if (java_script_builtin && |
+ code == kJavaScriptCallArgCountRegister.code()) { |
+ SNPrintF( |
+ str, |
+ "tagged argument count %s (will be untagged by continuation)\n", |
+ config->GetGeneralRegisterName(code)); |
+ } else { |
+ SNPrintF(str, "builtin register argument %s\n", |
+ config->GetGeneralRegisterName(code)); |
+ } |
+ DebugPrintOutputSlot(value, frame_index, output_frame_offset, |
+ str.start()); |
+ } |
+ } |
+ |
+ // Ensure the frame pointer register points to the callee's frame. The builtin |
+ // will build its own frame once we continue to it. |
+ Register fp_reg = JavaScriptFrame::fp_register(); |
+ output_frame->SetRegister(fp_reg.code(), output_[frame_index - 1]->GetFp()); |
+ |
+ Code* continue_to_builtin = |
+ java_script_builtin |
+ ? (must_handle_result |
+ ? isolate()->builtins()->builtin( |
+ Builtins::kContinueToJavaScriptBuiltinWithResult) |
+ : isolate()->builtins()->builtin( |
+ Builtins::kContinueToJavaScriptBuiltin)) |
+ : (must_handle_result |
+ ? isolate()->builtins()->builtin( |
+ Builtins::kContinueToCodeStubBuiltinWithResult) |
+ : isolate()->builtins()->builtin( |
+ Builtins::kContinueToCodeStubBuiltin)); |
+ output_frame->SetPc( |
+ reinterpret_cast<intptr_t>(continue_to_builtin->instruction_start())); |
+ |
+ Code* continuation = |
+ isolate()->builtins()->builtin(Builtins::kNotifyBuiltinContinuation); |
+ output_frame->SetContinuation( |
+ reinterpret_cast<intptr_t>(continuation->entry())); |
+} |
void Deoptimizer::MaterializeHeapObjects(JavaScriptFrameIterator* it) { |
// Walk to the last JavaScript output frame to find out if it has |
@@ -2405,6 +2697,24 @@ Handle<ByteArray> TranslationBuffer::CreateByteArray(Factory* factory) { |
return result; |
} |
+void Translation::BeginBuiltinContinuationFrame(BailoutId bailout_id, |
+ int literal_id, |
+ unsigned height) { |
+ buffer_->Add(BUILTIN_CONTINUATION_FRAME); |
+ buffer_->Add(bailout_id.ToInt()); |
+ buffer_->Add(literal_id); |
+ buffer_->Add(height); |
+} |
+ |
+void Translation::BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id, |
+ int literal_id, |
+ unsigned height) { |
+ buffer_->Add(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME); |
+ buffer_->Add(bailout_id.ToInt()); |
+ buffer_->Add(literal_id); |
+ buffer_->Add(height); |
+} |
+ |
void Translation::BeginConstructStubFrame(BailoutId bailout_id, int literal_id, |
unsigned height) { |
buffer_->Add(CONSTRUCT_STUB_FRAME); |
@@ -2607,6 +2917,8 @@ int Translation::NumberOfOperandsFor(Opcode opcode) { |
case JS_FRAME: |
case INTERPRETED_FRAME: |
case CONSTRUCT_STUB_FRAME: |
+ case BUILTIN_CONTINUATION_FRAME: |
+ case JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: |
return 3; |
case ARGUMENTS_ELEMENTS: |
case ARGUMENTS_LENGTH: |
@@ -3213,6 +3525,22 @@ TranslatedFrame TranslatedFrame::ConstructStubFrame( |
return frame; |
} |
+TranslatedFrame TranslatedFrame::BuiltinContinuationFrame( |
+ BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { |
+ base::OS::DebugBreak(); |
+ TranslatedFrame frame(kBuiltinContinuation, shared_info->GetIsolate(), |
+ shared_info, height); |
+ frame.node_id_ = bailout_id; |
+ return frame; |
+} |
+ |
+TranslatedFrame TranslatedFrame::JavaScriptBuiltinContinuationFrame( |
+ BailoutId bailout_id, SharedFunctionInfo* shared_info, int height) { |
+ TranslatedFrame frame(kJavaScriptBuiltinContinuation, |
+ shared_info->GetIsolate(), shared_info, height); |
+ frame.node_id_ = bailout_id; |
+ return frame; |
+} |
int TranslatedFrame::GetValueCount() { |
switch (kind()) { |
@@ -3238,6 +3566,8 @@ int TranslatedFrame::GetValueCount() { |
case kArgumentsAdaptor: |
case kConstructStub: |
+ case kBuiltinContinuation: |
+ case kJavaScriptBuiltinContinuation: |
return 1 + height_; |
case kTailCallerFunction: |
@@ -3341,6 +3671,38 @@ TranslatedFrame TranslatedState::CreateNextTranslatedFrame( |
height); |
} |
+ case Translation::BUILTIN_CONTINUATION_FRAME: { |
+ BailoutId bailout_id = BailoutId(iterator->Next()); |
+ SharedFunctionInfo* shared_info = |
+ SharedFunctionInfo::cast(literal_array->get(iterator->Next())); |
+ int height = iterator->Next(); |
+ if (trace_file != nullptr) { |
+ std::unique_ptr<char[]> name = shared_info->DebugName()->ToCString(); |
+ PrintF(trace_file, " reading builtin continuation frame %s", |
+ name.get()); |
+ PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n", |
+ bailout_id.ToInt(), height); |
+ } |
+ return TranslatedFrame::BuiltinContinuationFrame(bailout_id, shared_info, |
+ height); |
+ } |
+ |
+ case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: { |
+ BailoutId bailout_id = BailoutId(iterator->Next()); |
+ SharedFunctionInfo* shared_info = |
+ SharedFunctionInfo::cast(literal_array->get(iterator->Next())); |
+ int height = iterator->Next(); |
+ if (trace_file != nullptr) { |
+ std::unique_ptr<char[]> name = shared_info->DebugName()->ToCString(); |
+ PrintF(trace_file, " reading JavaScript builtin continuation frame %s", |
+ name.get()); |
+ PrintF(trace_file, " => bailout_id=%d, height=%d; inputs:\n", |
+ bailout_id.ToInt(), height); |
+ } |
+ return TranslatedFrame::JavaScriptBuiltinContinuationFrame( |
+ bailout_id, shared_info, height + 1); |
+ } |
+ |
case Translation::GETTER_STUB_FRAME: { |
SharedFunctionInfo* shared_info = |
SharedFunctionInfo::cast(literal_array->get(iterator->Next())); |
@@ -3509,6 +3871,8 @@ int TranslatedState::CreateNextTranslatedValue( |
case Translation::GETTER_STUB_FRAME: |
case Translation::SETTER_STUB_FRAME: |
case Translation::COMPILED_STUB_FRAME: |
+ case Translation::JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME: |
+ case Translation::BUILTIN_CONTINUATION_FRAME: |
// Peeled off before getting here. |
break; |