Commit bf84766a authored by Caitlin Potter's avatar Caitlin Potter Committed by Commit Bot

[CloneObjectIC] clone MutableHeapNumbers instead of referencing them

Adds a helper macro "CloneIfMutablePrimitive", which tests if the
operand is a MutableHeapNumber, and if so, clones it, otherwise
returning the original value.

Also modifies the signature of "CopyPropertyArrayValues" to take a
"DestroySource" enum, indicating whether or not the resulting object is
supplanting the source object or not, and removes all default
parameters from that macro (which were not used anyways).

This corrects the issue reported in chromium:901301, where
StaNamedOwnProperty was replacing the value of a MutableHeapNumber
referenced by both the cloned object and the source object.

BUG=chromium:901301, v8:7611
R=cbruni@chromium.org, jkummerow@chromium.org

Change-Id: I43df1ddc84dfa4840e680b6affeba452ce0b6629
Reviewed-on: https://chromium-review.googlesource.com/c/1318096
Commit-Queue: Caitlin Potter <caitp@igalia.com>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57304}
parent aacd3709
...@@ -3085,6 +3085,24 @@ TNode<MutableHeapNumber> CodeStubAssembler::AllocateMutableHeapNumber() { ...@@ -3085,6 +3085,24 @@ TNode<MutableHeapNumber> CodeStubAssembler::AllocateMutableHeapNumber() {
return UncheckedCast<MutableHeapNumber>(result); return UncheckedCast<MutableHeapNumber>(result);
} }
TNode<Object> CodeStubAssembler::CloneIfMutablePrimitive(TNode<Object> object) {
TVARIABLE(Object, result, object);
Label done(this);
GotoIf(TaggedIsSmi(object), &done);
GotoIfNot(IsMutableHeapNumber(UncheckedCast<HeapObject>(object)), &done);
{
// Mutable heap number found --- allocate a clone.
TNode<Float64T> value =
LoadHeapNumberValue(UncheckedCast<HeapNumber>(object));
result = AllocateMutableHeapNumberWithValue(value);
Goto(&done);
}
BIND(&done);
return result.value();
}
TNode<MutableHeapNumber> CodeStubAssembler::AllocateMutableHeapNumberWithValue( TNode<MutableHeapNumber> CodeStubAssembler::AllocateMutableHeapNumberWithValue(
SloppyTNode<Float64T> value) { SloppyTNode<Float64T> value) {
TNode<MutableHeapNumber> result = AllocateMutableHeapNumber(); TNode<MutableHeapNumber> result = AllocateMutableHeapNumber();
...@@ -5027,7 +5045,8 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array, ...@@ -5027,7 +5045,8 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array,
Node* to_array, Node* to_array,
Node* property_count, Node* property_count,
WriteBarrierMode barrier_mode, WriteBarrierMode barrier_mode,
ParameterMode mode) { ParameterMode mode,
DestroySource destroy_source) {
CSA_SLOW_ASSERT(this, MatchesParameterMode(property_count, mode)); CSA_SLOW_ASSERT(this, MatchesParameterMode(property_count, mode));
CSA_SLOW_ASSERT(this, Word32Or(IsPropertyArray(from_array), CSA_SLOW_ASSERT(this, Word32Or(IsPropertyArray(from_array),
IsEmptyFixedArray(from_array))); IsEmptyFixedArray(from_array)));
...@@ -5039,9 +5058,14 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array, ...@@ -5039,9 +5058,14 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array,
ElementsKind kind = PACKED_ELEMENTS; ElementsKind kind = PACKED_ELEMENTS;
BuildFastFixedArrayForEach( BuildFastFixedArrayForEach(
from_array, kind, start, property_count, from_array, kind, start, property_count,
[this, to_array, needs_write_barrier](Node* array, Node* offset) { [this, to_array, needs_write_barrier, destroy_source](Node* array,
Node* offset) {
Node* value = Load(MachineType::AnyTagged(), array, offset); Node* value = Load(MachineType::AnyTagged(), array, offset);
if (destroy_source == DestroySource::kNo) {
value = CloneIfMutablePrimitive(CAST(value));
}
if (needs_write_barrier) { if (needs_write_barrier) {
Store(to_array, offset, value); Store(to_array, offset, value);
} else { } else {
...@@ -5050,6 +5074,18 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array, ...@@ -5050,6 +5074,18 @@ void CodeStubAssembler::CopyPropertyArrayValues(Node* from_array,
} }
}, },
mode); mode);
#ifdef DEBUG
// Zap {from_array} if the copying above has made it invalid.
if (destroy_source == DestroySource::kYes) {
Label did_zap(this);
GotoIf(IsEmptyFixedArray(from_array), &did_zap);
FillPropertyArrayWithUndefined(from_array, start, property_count, mode);
Goto(&did_zap);
BIND(&did_zap);
}
#endif
Comment("] CopyPropertyArrayValues"); Comment("] CopyPropertyArrayValues");
} }
......
...@@ -1589,10 +1589,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1589,10 +1589,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* to_index, Node* to_index,
ParameterMode mode = INTPTR_PARAMETERS); ParameterMode mode = INTPTR_PARAMETERS);
void CopyPropertyArrayValues( enum class DestroySource { kNo, kYes };
Node* from_array, Node* to_array, Node* length,
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER, // Specify DestroySource::kYes if {from_array} is being supplanted by
ParameterMode mode = INTPTR_PARAMETERS); // {to_array}. This offers a slight performance benefit by simply copying the
// array word by word. The source may be destroyed at the end of this macro.
//
// Otherwise, specify DestroySource::kNo for operations where an Object is
// being cloned, to ensure that MutableHeapNumbers are unique between the
// source and cloned object.
void CopyPropertyArrayValues(Node* from_array, Node* to_array, Node* length,
WriteBarrierMode barrier_mode,
ParameterMode mode,
DestroySource destroy_source);
// Copies all elements from |from_array| of |length| size to // Copies all elements from |from_array| of |length| size to
// |to_array| of the same size respecting the elements kind. // |to_array| of the same size respecting the elements kind.
...@@ -3213,6 +3222,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -3213,6 +3222,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<JSArray> ArrayCreate(TNode<Context> context, TNode<Number> length); TNode<JSArray> ArrayCreate(TNode<Context> context, TNode<Number> length);
// Allocate a clone of a mutable primitive, if {object} is a
// MutableHeapNumber.
TNode<Object> CloneIfMutablePrimitive(TNode<Object> object);
private: private:
friend class CodeStubArguments; friend class CodeStubArguments;
......
...@@ -1684,7 +1684,8 @@ Node* AccessorAssembler::ExtendPropertiesBackingStore(Node* object, ...@@ -1684,7 +1684,8 @@ Node* AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
// |new_properties| is guaranteed to be in new space, so we can skip // |new_properties| is guaranteed to be in new space, so we can skip
// the write barrier. // the write barrier.
CopyPropertyArrayValues(var_properties.value(), new_properties, CopyPropertyArrayValues(var_properties.value(), new_properties,
var_length.value(), SKIP_WRITE_BARRIER, mode); var_length.value(), SKIP_WRITE_BARRIER, mode,
DestroySource::kYes);
// TODO(gsathya): Clean up the type conversions by creating smarter // TODO(gsathya): Clean up the type conversions by creating smarter
// helpers that do the correct op based on the mode. // helpers that do the correct op based on the mode.
...@@ -3620,7 +3621,7 @@ void AccessorAssembler::GenerateCloneObjectIC() { ...@@ -3620,7 +3621,7 @@ void AccessorAssembler::GenerateCloneObjectIC() {
auto mode = INTPTR_PARAMETERS; auto mode = INTPTR_PARAMETERS;
var_properties = CAST(AllocatePropertyArray(length, mode)); var_properties = CAST(AllocatePropertyArray(length, mode));
CopyPropertyArrayValues(source_properties, var_properties.value(), length, CopyPropertyArrayValues(source_properties, var_properties.value(), length,
SKIP_WRITE_BARRIER, mode); SKIP_WRITE_BARRIER, mode, DestroySource::kNo);
} }
Goto(&allocate_object); Goto(&allocate_object);
...@@ -3640,7 +3641,8 @@ void AccessorAssembler::GenerateCloneObjectIC() { ...@@ -3640,7 +3641,8 @@ void AccessorAssembler::GenerateCloneObjectIC() {
BuildFastLoop(source_start, source_size, BuildFastLoop(source_start, source_size,
[=](Node* field_index) { [=](Node* field_index) {
Node* field_offset = TimesPointerSize(field_index); Node* field_offset = TimesPointerSize(field_index);
Node* field = LoadObjectField(source, field_offset); TNode<Object> field = LoadObjectField(source, field_offset);
field = CloneIfMutablePrimitive(field);
Node* result_offset = Node* result_offset =
IntPtrAdd(field_offset, field_offset_difference); IntPtrAdd(field_offset, field_offset_difference);
StoreObjectFieldNoWriteBarrier(object, result_offset, StoreObjectFieldNoWriteBarrier(object, result_offset,
......
...@@ -99,3 +99,25 @@ ...@@ -99,3 +99,25 @@
// Megamorphic // Megamorphic
assertEquals({ boop: 1 }, f({ boop: 1 })); assertEquals({ boop: 1 }, f({ boop: 1 }));
})(); })();
// There are 2 paths in CloneObjectIC's handler which need to handle double
// fields specially --- in object properties, and copying the property array.
function testMutableInlineProperties() {
function inobject() { "use strict"; this.x = 1.1; }
const src = new inobject();
const x0 = src.x;
const clone = { ...src, x: x0 + 1 };
assertEquals(x0, src.x);
assertEquals({ x: 2.1 }, clone);
}
testMutableInlineProperties()
function testMutableOutOfLineProperties() {
const src = { a: 1, b: 2, c: 3 };
src.x = 2.3;
const x0 = src.x;
const clone = { ...src, x: x0 + 1 };
assertEquals(x0, src.x);
assertEquals({ a: 1, b: 2, c: 3, x: 3.3 }, clone);
}
testMutableOutOfLineProperties();
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