| Index: src/deoptimizer.cc
|
| diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc
|
| index 63d8462d1f9a8e86ef709c3c60d0dafcc6cd7d71..1e3ff146b5be80b615df6df0a8df9ac2deb85bdd 100644
|
| --- a/src/deoptimizer.cc
|
| +++ b/src/deoptimizer.cc
|
| @@ -502,7 +502,6 @@ const char* Deoptimizer::MessageFor(BailoutType type) {
|
| return NULL;
|
| }
|
|
|
| -
|
| Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction* function,
|
| BailoutType type, unsigned bailout_id, Address from,
|
| int fp_to_sp_delta, Code* optimized_code)
|
| @@ -513,11 +512,19 @@ Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction* function,
|
| from_(from),
|
| fp_to_sp_delta_(fp_to_sp_delta),
|
| has_alignment_padding_(0),
|
| + deoptimizing_throw_(false),
|
| + catch_handler_data_(-1),
|
| + catch_handler_pc_offset_(-1),
|
| input_(nullptr),
|
| output_count_(0),
|
| jsframe_count_(0),
|
| output_(nullptr),
|
| trace_scope_(nullptr) {
|
| + if (isolate->deoptimizer_lazy_throw()) {
|
| + isolate->set_deoptimizer_lazy_throw(false);
|
| + deoptimizing_throw_ = true;
|
| + }
|
| +
|
| // For COMPILED_STUBs called from builtins, the function pointer is a SMI
|
| // indicating an internal frame.
|
| if (function->IsSmi()) {
|
| @@ -691,6 +698,41 @@ int Deoptimizer::GetDeoptimizedCodeCount(Isolate* isolate) {
|
| return length;
|
| }
|
|
|
| +namespace {
|
| +
|
| +int LookupCatchHandler(TranslatedFrame* translated_frame, int* data_out) {
|
| + switch (translated_frame->kind()) {
|
| + case TranslatedFrame::kFunction: {
|
| + BailoutId node_id = translated_frame->node_id();
|
| + JSFunction* function =
|
| + JSFunction::cast(translated_frame->begin()->GetRawValue());
|
| + Code* non_optimized_code = function->shared()->code();
|
| + FixedArray* raw_data = non_optimized_code->deoptimization_data();
|
| + DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
|
| + unsigned pc_and_state =
|
| + Deoptimizer::GetOutputInfo(data, node_id, function->shared());
|
| + unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
|
| + HandlerTable* table =
|
| + HandlerTable::cast(non_optimized_code->handler_table());
|
| + HandlerTable::CatchPrediction prediction;
|
| + return table->LookupRange(pc_offset, data_out, &prediction);
|
| + }
|
| + case TranslatedFrame::kInterpretedFunction: {
|
| + int bytecode_offset = translated_frame->node_id().ToInt();
|
| + JSFunction* function =
|
| + JSFunction::cast(translated_frame->begin()->GetRawValue());
|
| + BytecodeArray* bytecode = function->shared()->bytecode_array();
|
| + HandlerTable* table = HandlerTable::cast(bytecode->handler_table());
|
| + HandlerTable::CatchPrediction prediction;
|
| + return table->LookupRange(bytecode_offset, data_out, &prediction);
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + return -1;
|
| +}
|
| +
|
| +} // namespace
|
|
|
| // We rely on this function not causing a GC. It is called from generated code
|
| // without having a real stack frame in place.
|
| @@ -731,6 +773,22 @@ void Deoptimizer::DoComputeOutputFrames() {
|
|
|
| // Do the input frame to output frame(s) translation.
|
| size_t count = translated_state_.frames().size();
|
| + // If we are supposed to go to the catch handler, find the catching frame
|
| + // for the catch and make sure we only deoptimize upto that frame.
|
| + if (deoptimizing_throw_) {
|
| + size_t catch_handler_frame_index = count;
|
| + for (size_t i = count; i-- > 0;) {
|
| + catch_handler_pc_offset_ = LookupCatchHandler(
|
| + &(translated_state_.frames()[i]), &catch_handler_data_);
|
| + if (catch_handler_pc_offset_ >= 0) {
|
| + catch_handler_frame_index = i;
|
| + break;
|
| + }
|
| + }
|
| + CHECK_LT(catch_handler_frame_index, count);
|
| + count = catch_handler_frame_index + 1;
|
| + }
|
| +
|
| DCHECK(output_ == NULL);
|
| output_ = new FrameDescription*[count];
|
| for (size_t i = 0; i < count; ++i) {
|
| @@ -749,11 +807,12 @@ void Deoptimizer::DoComputeOutputFrames() {
|
| int frame_index = static_cast<int>(i);
|
| switch (translated_state_.frames()[i].kind()) {
|
| case TranslatedFrame::kFunction:
|
| - DoComputeJSFrame(frame_index);
|
| + DoComputeJSFrame(frame_index, deoptimizing_throw_ && i == count - 1);
|
| jsframe_count_++;
|
| break;
|
| case TranslatedFrame::kInterpretedFunction:
|
| - DoComputeInterpretedFrame(frame_index);
|
| + DoComputeInterpretedFrame(frame_index,
|
| + deoptimizing_throw_ && i == count - 1);
|
| jsframe_count_++;
|
| break;
|
| case TranslatedFrame::kArgumentsAdaptor:
|
| @@ -798,19 +857,28 @@ void Deoptimizer::DoComputeOutputFrames() {
|
| }
|
| }
|
|
|
| -
|
| -void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| +void Deoptimizer::DoComputeJSFrame(int frame_index, bool goto_catch_handler) {
|
| TranslatedFrame* translated_frame =
|
| &(translated_state_.frames()[frame_index]);
|
| SharedFunctionInfo* shared = translated_frame->raw_shared_info();
|
|
|
| TranslatedFrame::iterator value_iterator = translated_frame->begin();
|
| + bool is_bottommost = (0 == frame_index);
|
| + bool is_topmost = (output_count_ - 1 == frame_index);
|
| int input_index = 0;
|
|
|
| BailoutId node_id = translated_frame->node_id();
|
| unsigned height =
|
| translated_frame->height() - 1; // Do not count the context.
|
| unsigned height_in_bytes = height * kPointerSize;
|
| + if (goto_catch_handler) {
|
| + // Take the stack height from the handler table.
|
| + height = catch_handler_data_;
|
| + // We also make space for the exception itself.
|
| + height_in_bytes = (height + 1) * kPointerSize;
|
| + DCHECK(is_topmost);
|
| + }
|
| +
|
| JSFunction* function = JSFunction::cast(value_iterator->GetRawValue());
|
| value_iterator++;
|
| input_index++;
|
| @@ -820,6 +888,8 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| PrintF(trace_scope_->file(), "%s", name.get());
|
| PrintF(trace_scope_->file(),
|
| " => node=%d, height=%d\n", node_id.ToInt(), height_in_bytes);
|
| + PrintF(trace_scope_->file(), " => node=%d, height=%d%s\n", node_id.ToInt(),
|
| + height_in_bytes, goto_catch_handler ? " (throw)" : "");
|
| }
|
|
|
| // The 'fixed' part of the frame consists of the incoming parameters and
|
| @@ -833,8 +903,6 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| new(output_frame_size) FrameDescription(output_frame_size, function);
|
| output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
|
|
|
| - 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;
|
| @@ -935,8 +1003,20 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| Register context_reg = JavaScriptFrame::context_register();
|
| output_offset -= kPointerSize;
|
| input_offset -= kPointerSize;
|
| +
|
| + TranslatedFrame::iterator context_pos = value_iterator;
|
| + int context_input_index = input_index;
|
| + // When deoptimizing into a catch block, we need to take the context
|
| + // from just above the top of the operand stack (we push the context
|
| + // at the entry of the try block).
|
| + if (goto_catch_handler) {
|
| + for (unsigned i = 0; i < height + 1; ++i) {
|
| + context_pos++;
|
| + context_input_index++;
|
| + }
|
| + }
|
| // Read the context from the translations.
|
| - Object* context = value_iterator->GetRawValue();
|
| + Object* context = context_pos->GetRawValue();
|
| if (context == isolate_->heap()->undefined_value()) {
|
| // If the context was optimized away, just use the context from
|
| // the activation. This should only apply to Crankshaft code.
|
| @@ -949,13 +1029,13 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| 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,
|
| + WriteValueToOutput(context, context_input_index, frame_index, output_offset,
|
| "context ");
|
| if (context == isolate_->heap()->arguments_marker()) {
|
| Address output_address =
|
| reinterpret_cast<Address>(output_[frame_index]->GetTop()) +
|
| output_offset;
|
| - values_to_materialize_.push_back({output_address, value_iterator});
|
| + values_to_materialize_.push_back({output_address, context_pos});
|
| }
|
| value_iterator++;
|
| input_index++;
|
| @@ -975,19 +1055,19 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| WriteTranslatedValueToOutput(&value_iterator, &input_index, frame_index,
|
| output_offset);
|
| }
|
| + if (goto_catch_handler) {
|
| + // Write out the exception for the catch handler.
|
| + output_offset -= kPointerSize;
|
| + Object* exception_obj = reinterpret_cast<Object*>(
|
| + input_->GetRegister(FullCodeGenerator::result_register().code()));
|
| + WriteValueToOutput(exception_obj, input_index, frame_index, output_offset,
|
| + "exception ");
|
| + input_index++;
|
| + }
|
| CHECK_EQ(0u, output_offset);
|
|
|
| - // Compute this frame's PC, state, and continuation.
|
| - Code* non_optimized_code = shared->code();
|
| - FixedArray* raw_data = non_optimized_code->deoptimization_data();
|
| - DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
|
| - Address start = non_optimized_code->instruction_start();
|
| - unsigned pc_and_state = GetOutputInfo(data, node_id, shared);
|
| - unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
|
| - intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
|
| - output_frame->SetPc(pc_value);
|
| -
|
| // Update constant pool.
|
| + Code* non_optimized_code = shared->code();
|
| if (FLAG_enable_embedded_constant_pool) {
|
| intptr_t constant_pool_value =
|
| reinterpret_cast<intptr_t>(non_optimized_code->constant_pool());
|
| @@ -999,8 +1079,22 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| }
|
| }
|
|
|
| + // Compute this frame's PC, state, and continuation.
|
| + FixedArray* raw_data = non_optimized_code->deoptimization_data();
|
| + DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
|
| + Address start = non_optimized_code->instruction_start();
|
| + unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
|
| + unsigned pc_offset = goto_catch_handler
|
| + ? catch_handler_pc_offset_
|
| + : FullCodeGenerator::PcField::decode(pc_and_state);
|
| + intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
|
| + output_frame->SetPc(pc_value);
|
| +
|
| + // If we are going to the catch handler, then the exception lives in
|
| + // the accumulator.
|
| FullCodeGenerator::State state =
|
| - FullCodeGenerator::StateField::decode(pc_and_state);
|
| + goto_catch_handler ? FullCodeGenerator::TOS_REG
|
| + : FullCodeGenerator::StateField::decode(pc_and_state);
|
| output_frame->SetState(Smi::FromInt(state));
|
|
|
| // Set the continuation for the topmost frame.
|
| @@ -1019,8 +1113,8 @@ void Deoptimizer::DoComputeJSFrame(int frame_index) {
|
| }
|
| }
|
|
|
| -
|
| -void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| +void Deoptimizer::DoComputeInterpretedFrame(int frame_index,
|
| + bool goto_catch_handler) {
|
| TranslatedFrame* translated_frame =
|
| &(translated_state_.frames()[frame_index]);
|
| SharedFunctionInfo* shared = translated_frame->raw_shared_info();
|
| @@ -1028,7 +1122,7 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| TranslatedFrame::iterator value_iterator = translated_frame->begin();
|
| int input_index = 0;
|
|
|
| - BailoutId bytecode_offset = translated_frame->node_id();
|
| + int bytecode_offset = translated_frame->node_id().ToInt();
|
| unsigned height = translated_frame->height();
|
| unsigned height_in_bytes = height * kPointerSize;
|
| JSFunction* function = JSFunction::cast(value_iterator->GetRawValue());
|
| @@ -1038,8 +1132,12 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| PrintF(trace_scope_->file(), " translating interpreted frame ");
|
| base::SmartArrayPointer<char> name = shared->DebugName()->ToCString();
|
| PrintF(trace_scope_->file(), "%s", name.get());
|
| - PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d\n",
|
| - bytecode_offset.ToInt(), height_in_bytes);
|
| + PrintF(trace_scope_->file(), " => bytecode_offset=%d, height=%d%s\n",
|
| + bytecode_offset, height_in_bytes,
|
| + goto_catch_handler ? " (throw)" : "");
|
| + }
|
| + if (goto_catch_handler) {
|
| + bytecode_offset = catch_handler_pc_offset_;
|
| }
|
|
|
| // The 'fixed' part of the frame consists of the incoming parameters and
|
| @@ -1151,14 +1249,27 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| Register context_reg = InterpretedFrame::context_register();
|
| output_offset -= kPointerSize;
|
| input_offset -= kPointerSize;
|
| +
|
| + // When deoptimizing into a catch block, we need to take the context
|
| + // from a register that was specified in the handler table.
|
| + TranslatedFrame::iterator context_pos = value_iterator;
|
| + int context_input_index = input_index;
|
| + if (goto_catch_handler) {
|
| + // Skip to the translated value of the register specified
|
| + // in the handler table.
|
| + for (int i = 0; i < catch_handler_data_ + 1; ++i) {
|
| + context_pos++;
|
| + context_input_index++;
|
| + }
|
| + }
|
| // Read the context from the translations.
|
| - Object* context = value_iterator->GetRawValue();
|
| + Object* context = context_pos->GetRawValue();
|
| // The context should not be a placeholder for a materialized object.
|
| CHECK(context != isolate_->heap()->arguments_marker());
|
| 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,
|
| + WriteValueToOutput(context, context_input_index, frame_index, output_offset,
|
| "context ");
|
| value_iterator++;
|
| input_index++;
|
| @@ -1191,7 +1302,7 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| output_offset -= kPointerSize;
|
| input_offset -= kPointerSize;
|
| int raw_bytecode_offset =
|
| - BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset.ToInt();
|
| + BytecodeArray::kHeaderSize - kHeapObjectTag + bytecode_offset;
|
| Smi* smi_bytecode_offset = Smi::FromInt(raw_bytecode_offset);
|
| WriteValueToOutput(smi_bytecode_offset, 0, frame_index, output_offset,
|
| "bytecode offset ");
|
| @@ -1204,10 +1315,15 @@ void Deoptimizer::DoComputeInterpretedFrame(int frame_index) {
|
| }
|
| CHECK_EQ(0u, output_offset);
|
|
|
| - // Set the accumulator register.
|
| - output_frame->SetRegister(
|
| - kInterpreterAccumulatorRegister.code(),
|
| - reinterpret_cast<intptr_t>(value_iterator->GetRawValue()));
|
| + // Set the accumulator register. If we are lazy deopting to a catch handler,
|
| + // we set the accumulator to the exception (which lives in the result
|
| + // register).
|
| + intptr_t accumulator_value =
|
| + goto_catch_handler
|
| + ? input_->GetRegister(FullCodeGenerator::result_register().code())
|
| + : reinterpret_cast<intptr_t>(value_iterator->GetRawValue());
|
| + output_frame->SetRegister(kInterpreterAccumulatorRegister.code(),
|
| + accumulator_value);
|
| value_iterator++;
|
|
|
| Builtins* builtins = isolate_->builtins();
|
|
|