Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.29',
'v8_embedder_string': '-node.32',

##### V8 defaults for Node.js #####

Expand Down
25 changes: 25 additions & 0 deletions deps/v8/src/deoptimizer/translated-state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,31 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) {
}
}

// static
std::optional<Tagged<Object>> TranslatedState::TryResolveTaggedValue(
DeoptTranslationIterator* it, Address fp,
Tagged<DeoptimizationLiteralArray> literals) {
TranslationOpcode opcode = it->NextOpcode();
switch (opcode) {
case TranslationOpcode::LITERAL: {
int literal_index = it->NextOperand();
return literals->get(literal_index);
}
case TranslationOpcode::TAGGED_STACK_SLOT: {
int slot_offset =
OptimizedJSFrame::StackSlotOffsetRelativeToFp(it->NextOperand());
intptr_t value = *reinterpret_cast<intptr_t*>(fp + slot_offset);
return Tagged<Object>(DecompressIfNeeded(value));
}
default:
// Any other encoding (unboxed numerics, register-resident values,
// captured objects, etc.) requires the full TranslatedState path to
// materialize. Caller should fall back.
it->SkipOperands(TranslationOpcodeOperandCount(opcode));
return std::nullopt;
}
}

TranslatedState::TranslatedState(const JavaScriptFrame* frame)
: purpose_(kFrameInspection) {
int deopt_index = SafepointEntry::kNoDeoptIndex;
Expand Down
12 changes: 11 additions & 1 deletion deps/v8/src/deoptimizer/translated-state.h
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,17 @@ class TranslatedState {
void VerifyMaterializedObjects();
bool DoUpdateFeedback(DeoptimizeReason reason);

// Resolves one deopt translation value opcode to a raw Tagged<Object>,
// reading from the live frame if needed. Only LITERAL and
// TAGGED_STACK_SLOT can be resolved cheaply; for any other opcode the
// iterator is left positioned just past the opcode and std::nullopt is
// returned so the caller can fall back to the full materialization path.
static std::optional<Tagged<Object>> TryResolveTaggedValue(
DeoptTranslationIterator* it, Address fp,
Tagged<DeoptimizationLiteralArray> literals);

static Address DecompressIfNeeded(intptr_t value);

private:
friend TranslatedValue;

Expand All @@ -529,7 +540,6 @@ class TranslatedState {
int frame_index, DeoptTranslationIterator* iterator,
const DeoptimizationLiteralProvider& literal_array, Address fp,
RegisterValues* registers, FILE* trace_file);
Address DecompressIfNeeded(intptr_t value);
void CreateArgumentsElementsTranslatedValues(int frame_index,
Address input_frame_pointer,
CreateArgumentsType type,
Expand Down
153 changes: 141 additions & 12 deletions deps/v8/src/execution/frames.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3179,7 +3179,118 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const {
"Missing deoptimization information for OptimizedJSFrame::Summarize.");
}

// Prepare iteration over translation. We must not materialize values here
// Lightweight walk: iterate frame headers only, resolving just the
// function and receiver from the live frame via ResolveTaggedValue.
// This avoids the expensive TranslatedState::Init + Prepare path that
// would parse every value in every inlined frame.

Tagged<DeoptimizationLiteralArray> literal_array = data->LiteralArray();

// Lightweight walk: resolve function and receiver from live frame headers
// and build JavaScriptFrameSummary objects directly.
bool needs_full_walk = false;
{
DisallowGarbageCollection no_gc;
DeoptimizationFrameTranslation::Iterator it(
data->FrameTranslation(), data->TranslationIndex(deopt_index).value());
bool is_constructor = IsConstructor();
int remaining = it.EnterBeginOpcode().total_frame_count;

while (remaining > 0) {
TranslationOpcode opcode = it.SeekNextFrame();
remaining--;

if (opcode == TranslationOpcode::CONSTRUCT_CREATE_STUB_FRAME ||
opcode == TranslationOpcode::CONSTRUCT_INVOKE_STUB_FRAME) {
is_constructor = true;
it.SkipOperands(TranslationOpcodeOperandCount(opcode));
continue;
}

if (!IsTranslationJsFrameOpcode(opcode)) {
#if V8_ENABLE_WEBASSEMBLY
// Wasm-inlined-into-JS frames need the full TranslatedState
// machinery to produce WasmFrameSummary entries.
if (opcode == TranslationOpcode::WASM_INLINED_INTO_JS_FRAME) {
needs_full_walk = true;
break;
}
#endif
it.SkipOperands(TranslationOpcodeOperandCount(opcode));
continue;
}

bool is_builtin_cont =
(opcode == TranslationOpcode::JAVASCRIPT_BUILTIN_CONTINUATION_FRAME ||
opcode == TranslationOpcode::
JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME);

int bytecode_offset = it.NextOperand();
int sfi_id = it.NextOperand();
Tagged<SharedFunctionInfo> sfi =
Cast<SharedFunctionInfo>(literal_array->get(sfi_id));

// Skip remaining header operands to reach the values.
it.SkipOperands(TranslationOpcodeOperandCount(opcode) - 2);

// Resolve closure and receiver from the live frame. The closure is
// always tagged (LITERAL or TAGGED_STACK_SLOT), but the receiver is
// just parameter 0 of the (possibly inlined) frame and may be encoded
// in any representation the optimizer chose (e.g. DOUBLE_STACK_SLOT
// for an unboxed Float64). Fall back to the full materialization path
// in that case.
std::optional<Tagged<Object>> function_obj =
TranslatedState::TryResolveTaggedValue(&it, fp(), literal_array);
DCHECK(function_obj.has_value());
DCHECK(IsJSFunction(*function_obj));
std::optional<Tagged<Object>> receiver_obj =
TranslatedState::TryResolveTaggedValue(&it, fp(), literal_array);
if (!receiver_obj.has_value()) {
needs_full_walk = true;
break;
}

Tagged<AbstractCode> abstract_code;
int code_offset;
if (is_builtin_cont) {
code_offset = 0;
abstract_code = Cast<AbstractCode>(
isolate()->builtins()->code(Builtins::GetBuiltinFromBytecodeOffset(
BytecodeOffset(bytecode_offset))));
} else {
code_offset = bytecode_offset;
abstract_code = Cast<AbstractCode>(sfi->GetBytecodeArray(isolate()));
}

DirectHandle<FixedArray> params = GetParameters(never_allocate);
FrameSummary::JavaScriptFrameSummary summary(
isolate(), *receiver_obj, Cast<JSFunction>(*function_obj),
abstract_code, code_offset, is_constructor, *params);
summaries.frames.push_back(summary);
is_constructor = false;
}

if (!needs_full_walk && is_constructor) {
summaries.top_frame_is_construct_call = true;
}
} // no_gc scope ends.

if (needs_full_walk) {
return SummarizeFull(data, deopt_index, never_allocate);
}

return summaries;
}

FrameSummaries OptimizedJSFrame::SummarizeFull(Tagged<DeoptimizationData> data,
int deopt_index,
bool never_allocate) const {
FrameSummaries summaries;

DCHECK_NE(deopt_index, SafepointEntry::kNoDeoptIndex);
DCHECK(!data.is_null());

// Prepare iteration over translation. We must not materialize values here
// because we do not deoptimize the function.
TranslatedState translated(this);
translated.Prepare(fp());
Expand All @@ -3204,7 +3315,21 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const {
// Get the correct receiver in the optimized frame.
static_assert(TranslatedFrame::kReceiverIsFirstParameterInJSFrames);
CHECK(!translated_values->IsMaterializedObject());
DirectHandle<Object> receiver = translated_values->GetValue();
// Check GetRawValue() against arguments_marker() first to see whether
// calling GetValue() allocates.
Tagged<Object> receiver_obj = translated_values->GetRawValue();
DirectHandle<Object> receiver;
if (receiver_obj == ReadOnlyRoots(isolate()).arguments_marker() &&
never_allocate) {
// Calling GetValue() would definitely trigger allocation but with
// `never_allocate` allocations are not allowed. Simply pick `undefined`
// as receiver instead even though it is off. `never_allocate` is
// currently only used for OOM stacks, where we don't even emit the
// receiver but want to see as many stack frames as possible.
receiver = isolate()->factory()->undefined_value();
} else {
receiver = translated_values->GetValue();
}
translated_values++;

// Determine the underlying code object and the position within it from
Expand Down Expand Up @@ -3311,24 +3436,20 @@ int TurbofanJSFrame::FindReturnPCForTrampoline(Tagged<Code> code,
return safepoints.find_return_pc(trampoline_pc);
}

Tagged<DeoptimizationData> OptimizedJSFrame::GetDeoptimizationData(
Tagged<Code> code, int* deopt_index) const {
DCHECK(is_optimized());

Address pc = maybe_unauthenticated_pc();

DCHECK(code->contains(isolate(), pc));
// static
Tagged<DeoptimizationData> OptimizedJSFrame::GetDeoptimizationDataForPC(
Isolate* isolate, Tagged<Code> code, Address pc, int* deopt_index) {
DCHECK(code->contains(isolate, pc));
DCHECK(CodeKindCanDeoptimize(code->kind()));

if (code->is_maglevved()) {
MaglevSafepointEntry safepoint_entry =
code->GetMaglevSafepointEntry(isolate(), pc);
code->GetMaglevSafepointEntry(isolate, pc);
if (safepoint_entry.has_deoptimization_index()) {
*deopt_index = safepoint_entry.deoptimization_index();
return code->deoptimization_data();
}
} else {
SafepointEntry safepoint_entry = code->GetSafepointEntry(isolate(), pc);
SafepointEntry safepoint_entry = code->GetSafepointEntry(isolate, pc);
if (safepoint_entry.has_deoptimization_index()) {
*deopt_index = safepoint_entry.deoptimization_index();
return code->deoptimization_data();
Expand All @@ -3338,6 +3459,14 @@ Tagged<DeoptimizationData> OptimizedJSFrame::GetDeoptimizationData(
return {};
}

Tagged<DeoptimizationData> OptimizedJSFrame::GetDeoptimizationData(
Tagged<Code> code, int* deopt_index) const {
DCHECK(is_optimized());
Address pc = maybe_unauthenticated_pc();
DCHECK(code->contains(isolate(), pc));
return GetDeoptimizationDataForPC(isolate(), code, pc, deopt_index);
}

void OptimizedJSFrame::GetFunctions(
std::vector<Tagged<SharedFunctionInfo>>* functions) const {
DCHECK(functions->empty());
Expand Down
12 changes: 12 additions & 0 deletions deps/v8/src/execution/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,11 @@ class OptimizedJSFrame : public JavaScriptFrame {
Tagged<DeoptimizationData> GetDeoptimizationData(Tagged<Code> code,
int* deopt_index) const;

// Like GetDeoptimizationData, but takes an explicit PC instead of reading
// it from the frame. Can be used without a live frame.
static Tagged<DeoptimizationData> GetDeoptimizationDataForPC(
Isolate* isolate, Tagged<Code> code, Address pc, int* deopt_index);

static int StackSlotOffsetRelativeToFp(int slot_index);

// Lookup exception handler for current {pc}, returns -1 if none found.
Expand All @@ -1178,6 +1183,13 @@ class OptimizedJSFrame : public JavaScriptFrame {
virtual int FindReturnPCForTrampoline(Tagged<Code> code,
int trampoline_pc) const = 0;

private:
// Full TranslatedState-based walk, used as fallback when the lightweight
// path in Summarize() encounters frames it cannot handle (e.g.
// wasm-inlined-into-JS frames).
FrameSummaries SummarizeFull(Tagged<DeoptimizationData> data, int deopt_index,
bool never_allocate) const;

protected:
inline explicit OptimizedJSFrame(StackFrameIteratorBase* iterator);
};
Expand Down
Loading
Loading