Commit a5ab3507 authored by Simon Zünd's avatar Simon Zünd Committed by V8 LUCI CQ

[deoptimizer] Support 'restart frame' in the deoptimizer

Doc: https://bit.ly/revive-restart-frame

This CL implements support for the Debugger's "restart frame"
functionality in the deoptimizer. When the debugger wants to restart
a frame, we throw a termination exception.

If the restarted frame is an optimized frame or was inlined into
an optimized frame, the deoptimizer has to materialize all the
frames up to (and including) the frame we want to restart. This
is similar to materializing all the frames up until the frame
with a catch handler.

The main difference is that we do not jump into the middle of the
top-most materialized frame, but instead use the
RestartFrameTrampolone to immediatly exit, and then re-invoke the
top-most materialized frame.

R=jarin@chromium.org, tebbi@chromium.org

Bug: chromium:1303521
Change-Id: I74ee412bc67f027be81fe56e529b5e5161e97153
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3616504Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80462}
parent cbc0e0a1
......@@ -404,6 +404,9 @@ class V8_EXPORT_PRIVATE Debug {
thread_local_.restart_frame_id_ = StackFrameId::NO_ID;
thread_local_.restart_inline_frame_index_ = -1;
}
int restart_inline_frame_index() const {
return thread_local_.restart_inline_frame_index_;
}
inline bool break_disabled() const { return break_disabled_; }
......
......@@ -8,6 +8,7 @@
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/reloc-info.h"
#include "src/debug/debug.h"
#include "src/deoptimizer/deoptimized-frame-info.h"
#include "src/deoptimizer/materialized-object-store.h"
#include "src/execution/frames-inl.h"
......@@ -469,6 +470,7 @@ Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction function,
deoptimizing_throw_(false),
catch_handler_data_(-1),
catch_handler_pc_offset_(-1),
restart_frame_index_(-1),
input_(nullptr),
output_count_(0),
output_(nullptr),
......@@ -487,6 +489,13 @@ Deoptimizer::Deoptimizer(Isolate* isolate, JSFunction function,
deoptimizing_throw_ = true;
}
if (isolate->debug()->IsRestartFrameScheduled()) {
CHECK(deoptimizing_throw_);
restart_frame_index_ = isolate->debug()->restart_inline_frame_index();
CHECK_GE(restart_frame_index_, 0);
isolate->debug()->clear_restart_frame();
}
DCHECK_NE(from, kNullAddress);
compiled_code_ = FindOptimizedCode();
DCHECK(!compiled_code_.is_null());
......@@ -827,9 +836,13 @@ 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 up to that frame.
if (deoptimizing_throw_) {
if (is_restart_frame()) {
// If the debugger requested to restart a particular frame, only materialize
// up to that frame.
count = restart_frame_index_ + 1;
} else if (deoptimizing_throw_) {
// If we are supposed to go to the catch handler, find the catching frame
// for the catch and make sure we only deoptimize up to that frame.
size_t catch_handler_frame_index = count;
for (size_t i = count; i-- > 0;) {
catch_handler_pc_offset_ = LookupCatchHandler(
......@@ -920,7 +933,10 @@ void Deoptimizer::DoComputeOutputFrames() {
namespace {
// Get the dispatch builtin for unoptimized frames.
Builtin DispatchBuiltinFor(bool is_baseline, bool advance_bc) {
Builtin DispatchBuiltinFor(bool is_baseline, bool advance_bc,
bool is_restart_frame) {
if (is_restart_frame) return Builtin::kRestartFrameTrampoline;
if (is_baseline) {
return advance_bc ? Builtin::kBaselineOrInterpreterEnterAtNextBytecode
: Builtin::kBaselineOrInterpreterEnterAtBytecode;
......@@ -988,8 +1004,9 @@ void Deoptimizer::DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame,
(!is_topmost || (deopt_kind_ == DeoptimizeKind::kLazy)) &&
!goto_catch_handler;
const bool is_baseline = shared.HasBaselineCode();
Code dispatch_builtin =
FromCodeT(builtins->code(DispatchBuiltinFor(is_baseline, advance_bc)));
const bool restart_frame = goto_catch_handler && is_restart_frame();
Code dispatch_builtin = FromCodeT(builtins->code(
DispatchBuiltinFor(is_baseline, advance_bc, restart_frame)));
if (verbose_tracing_enabled()) {
PrintF(trace_scope()->file(), " translating %s frame ",
......
......@@ -195,6 +195,8 @@ class Deoptimizer : public Malloced {
static void TraceDeoptAll(Isolate* isolate);
static void TraceDeoptMarked(Isolate* isolate);
bool is_restart_frame() const { return restart_frame_index_ >= 0; }
Isolate* isolate_;
JSFunction function_;
Code compiled_code_;
......@@ -206,6 +208,7 @@ class Deoptimizer : public Malloced {
bool deoptimizing_throw_;
int catch_handler_data_;
int catch_handler_pc_offset_;
int restart_frame_index_;
// Input frame description.
FrameDescription* input_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment