diff --git a/common.gypi b/common.gypi index de80a68f492f..eeebab06807f 100644 --- a/common.gypi +++ b/common.gypi @@ -44,7 +44,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.37', + 'v8_embedder_string': '-node.38', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/maglev/maglev-code-generator.cc b/deps/v8/src/maglev/maglev-code-generator.cc index 5cd614bd3bf2..ebea2905d39c 100644 --- a/deps/v8/src/maglev/maglev-code-generator.cc +++ b/deps/v8/src/maglev/maglev-code-generator.cc @@ -1499,17 +1499,9 @@ class MaglevFrameTranslationBuilder { return kNotDuplicated; } - void BuildHeapNumber(const VirtualObject* vobject) { - DCHECK_EQ(vobject->object_type(), vobj::ObjectType::kHeapNumber); - ValueNode* value_node = vobject->get(HeapNumber::kValueOffset); - return BuildHeapNumber(value_node->Cast()->value()); - } - - void BuildHeapNumber(Float64 number) { - DirectHandle value = - local_isolate_->factory()->NewHeapNumberFromBits( - number.get_bits()); - translation_array_builder_->StoreLiteral(GetDeoptLiteral(*value)); + int CreateUnduplicatableId() { + object_ids_.push_back(kNotDuplicated); + return kNotDuplicated; } void BuildNestedValue(const ValueNode* value, @@ -1564,12 +1556,16 @@ class MaglevFrameTranslationBuilder { const InputLocation*& input_location, const VirtualObjectList& virtual_objects) { vobj::ObjectType object_type = object->object_type(); - if (object_type == vobj::ObjectType::kHeapNumber) { - // TODO(jgruber): Could we use the standard path below instead? - return BuildHeapNumber(object); - } + DCHECK_NOT_NULL(object->allocation()); + // HeapNumbers may be mutable object fields; each materialization must + // create a fresh box, so they are never deduplicated. + // TODO(victorgomes): Constrain which objects may contain mutable + // HeapNumbers. Immutable HeapNumbers can be stored as a literal object + // instead of a captured object. int dup_id = - GetDuplicatedId(reinterpret_cast(object->allocation())); + object_type == vobj::ObjectType::kHeapNumber + ? CreateUnduplicatableId() + : GetDuplicatedId(reinterpret_cast(object->allocation())); if (dup_id != kNotDuplicated) { translation_array_builder_->DuplicateObject(dup_id); object->ForEachNestedRuntimeInput( @@ -1708,7 +1704,7 @@ class MaglevFrameTranslationBuilder { IdentityMap* protected_deopt_literals_; IdentityMap* deopt_literals_; - static const int kNotDuplicated = -1; + static constexpr int kNotDuplicated = -1; std::vector object_ids_; }; diff --git a/deps/v8/src/maglev/maglev-ir.h b/deps/v8/src/maglev/maglev-ir.h index d6282a00ebb3..d3720049d225 100644 --- a/deps/v8/src/maglev/maglev-ir.h +++ b/deps/v8/src/maglev/maglev-ir.h @@ -5622,11 +5622,9 @@ class VirtualObject : public FixedInputValueNodeT<0, VirtualObject> { vobj::Field snd = FieldForOffset(ConsString::kSecondOffset); return callback(slots_[snd.slot_index], snd); } - if (object_type() == vobj::ObjectType::kHeapNumber) { - // HeapNumber materialization creates a literal object instead of - // slot traversal. - return true; - } + // TODO(victorgomes): Constrain which objects may contain mutable + // HeapNumbers. Immutable HeapNumbers can be stored as a literal object + // instead of traversing their slots. } for (int i = 0; i < slot_count(); i++) { if (!callback(slots_[i], FieldForSlot(i))) { @@ -5917,8 +5915,8 @@ struct VirtualPrimitiveHeapObjectShape : VirtualHeapObjectShape {}; struct VirtualHeapNumberShape : VirtualPrimitiveHeapObjectShape { using T = HeapNumber; - // Special handling needed; deopt materialization uses a special path. - // TODO(jgruber): .. but could it take the standard path instead? + // Special handling needed; instances may be mutable object fields and thus + // must never be deduplicated in deopt frames. static constexpr vobj::ObjectType kObjectType = vobj::ObjectType::kHeapNumber; #define FIELD_LIST(V) V(value, T::kValueOffset, vobj::FieldType::kFloat64) DEF_SHAPE(VirtualPrimitiveHeapObjectShape, FIELD_LIST); diff --git a/deps/v8/test/mjsunit/maglev/regress-547819997.js b/deps/v8/test/mjsunit/maglev/regress-547819997.js new file mode 100644 index 000000000000..43a8e395d3fc --- /dev/null +++ b/deps/v8/test/mjsunit/maglev/regress-547819997.js @@ -0,0 +1,38 @@ +// Copyright 2026 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax --maglev + +const boxes = []; + +// Never called during warm-up, so this call site has no feedback and Maglev +// emits an unconditional deopt for it. The literal below is therefore only +// used by deopt frames and gets elided by escape analysis. +function sink(o) { + boxes.push(o); +} + +function foo(depth, take) { + const o = {x: 1.5}; + if (depth > 0) foo(depth - 1, take); + if (take) sink(o); +} + +%PrepareFunctionForOptimization(foo); +foo(3, false); +foo(3, false); +%OptimizeMaglevOnNextCall(foo); +foo(3, false); + +// Deopts the innermost activation eagerly and the outer ones lazily, so every +// activation materializes its own object. +foo(3, true); + +assertEquals(4, boxes.length); +for (let i = 0; i < boxes.length; i++) { + boxes[i].x = i; +} +for (let i = 0; i < boxes.length; i++) { + assertEquals(i, boxes[i].x); +}