deps: V8: backport 1a0089053443

Original commit message:

    [stack traces] Fall back to full walk when receiver is unboxed

    The lightweight OptimizedJSFrame::Summarize path added in
    crrev.com/c/7722138 assumed that closure and receiver are always
    encoded as LITERAL or TAGGED_STACK_SLOT in the deopt translation.
    This holds for the closure (a JSFunction reference is always tagged)
    but not for the receiver: the "receiver" slot in an InterpretedFrame
    translation is just parameter 0 of the (possibly inlined) frame and
    is emitted via BuildDeoptFrameSingleValue, which encodes it according
    to the value's representation. An inlined frame whose `this` was
    typed as Float64 ends up as DOUBLE_STACK_SLOT, hitting UNREACHABLE in
    ResolveTaggedValue.

    Rename ResolveTaggedValue to TryResolveTaggedValue, return
    std::optional, and fall back to SummarizeFull when the receiver isn't
    a directly-resolvable tagged value.

    Fixed: 499260582
    Change-Id: I1f9cdd28e4b6b76a253a46e43b248d9239a4ecd8
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7748309
    Auto-Submit: Jakob Linke <jgruber@chromium.org>
    Reviewed-by: Leszek Swirski <leszeks@chromium.org>
    Commit-Queue: Leszek Swirski <leszeks@chromium.org>
    Commit-Queue: Jakob Linke <jgruber@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#106423}

Refs: https://github.com/v8/v8/commit/1a0089053443ae4001fd15b84f1b46f330a0b3fa
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65764
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
This commit is contained in:
Jakob LinkeandAntoine du Hamel authored and Node.js GitHub Bot committed 2026-09-07 12:34:51 +00:00
1 parent 7a4654c501
commit a3f0041e8f
5 files changed
+48 -13

No files matched your search

+1 -1
View File
@@ -43,7 +43,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.31',
'v8_embedder_string': '-node.32',
##### V8 defaults for Node.js #####
+6 -2
View File
@@ -1890,7 +1890,7 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) {
}
// static
Tagged<Object> TranslatedState::ResolveTaggedValue(
std::optional<Tagged<Object>> TranslatedState::TryResolveTaggedValue(
DeoptTranslationIterator* it, Address fp,
Tagged<DeoptimizationLiteralArray> literals) {
TranslationOpcode opcode = it->NextOpcode();
@@ -1906,7 +1906,11 @@ Tagged<Object> TranslatedState::ResolveTaggedValue(
return Tagged<Object>(DecompressIfNeeded(value));
}
default:
UNREACHABLE();
// 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;
}
}
+4 -2
View File
@@ -513,8 +513,10 @@ class TranslatedState {
// Resolves one deopt translation value opcode to a raw Tagged<Object>,
// reading from the live frame if needed. Only LITERAL and
// TAGGED_STACK_SLOT are expected; other opcodes are UNREACHABLE.
static Tagged<Object> ResolveTaggedValue(
// 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);
+17 -8
View File
@@ -3233,13 +3233,22 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const {
// Skip remaining header operands to reach the values.
it.SkipOperands(TranslationOpcodeOperandCount(opcode) - 2);
// Resolve closure and receiver from the live frame. Both are always
// encoded as LITERAL or TAGGED_STACK_SLOT in the deopt translation.
Tagged<Object> function_obj =
TranslatedState::ResolveTaggedValue(&it, fp(), literal_array);
DCHECK(IsJSFunction(function_obj));
Tagged<Object> receiver_obj =
TranslatedState::ResolveTaggedValue(&it, fp(), literal_array);
// 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;
@@ -3255,7 +3264,7 @@ FrameSummaries OptimizedJSFrame::Summarize(bool never_allocate) const {
DirectHandle<FixedArray> params = GetParameters(never_allocate);
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver_obj, Cast<JSFunction>(function_obj),
isolate(), *receiver_obj, Cast<JSFunction>(*function_obj),
abstract_code, code_offset, is_constructor, *params);
summaries.frames.push_back(summary);
is_constructor = false;
+20
View File
@@ -0,0 +1,20 @@
// Copyright 2026 the V8 project authors. All rights reserved.
// Use of this source code governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --jit-fuzzing --single-threaded
function f9(a10, a11) {
'use strict';
for (let v12 = 0; v12 < 5; v12++) {
const v15 = ({ maxByteLength: 1073741824 }).maxByteLength;
%OptimizeOsr();
function F16(a18, a19, a20) {
if (!new.target) { throw 'must be called with new'; }
}
try { F16.call(v15); } catch (e) {}
}
return f9;
}
%PrepareFunctionForOptimization(f9);
f9(f9, f9);