Commit 467b70c9 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[runtime] Support fast cloning of object literal elements

BUG: v8:6211
Change-Id: Ief28872f6ce97ff326f9a86367f872e321b2612a
Bug: 
Reviewed-on: https://chromium-review.googlesource.com/508650
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45448}
parent 536a5cd2
...@@ -1374,7 +1374,7 @@ class ObjectLiteral final : public MaterializedLiteral { ...@@ -1374,7 +1374,7 @@ class ObjectLiteral final : public MaterializedLiteral {
} }
bool has_elements() const { return HasElementsField::decode(bit_field_); } bool has_elements() const { return HasElementsField::decode(bit_field_); }
bool has_shallow_properties() const { bool has_shallow_properties() const {
return depth() == 1 && !has_elements() && !may_store_doubles(); return depth() == 1 && !may_store_doubles();
} }
bool has_rest_property() const { bool has_rest_property() const {
return HasRestPropertyField::decode(bit_field_); return HasRestPropertyField::decode(bit_field_);
......
...@@ -28,6 +28,36 @@ void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) { ...@@ -28,6 +28,36 @@ void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
typedef compiler::Node Node; typedef compiler::Node Node;
Node* ConstructorBuiltinsAssembler::CopyFixedArrayBase(Node* fixed_array) {
Label if_fixed_array(this), if_fixed_double_array(this), done(this);
VARIABLE(result, MachineRepresentation::kTagged);
Node* capacity = LoadAndUntagFixedArrayBaseLength(fixed_array);
Branch(IsFixedDoubleArrayMap(LoadMap(fixed_array)), &if_fixed_double_array,
&if_fixed_array);
BIND(&if_fixed_double_array);
{
ElementsKind kind = FAST_DOUBLE_ELEMENTS;
Node* copy = AllocateFixedArray(kind, capacity);
CopyFixedArrayElements(kind, fixed_array, kind, copy, capacity, capacity,
SKIP_WRITE_BARRIER);
result.Bind(copy);
Goto(&done);
}
BIND(&if_fixed_array);
{
ElementsKind kind = FAST_ELEMENTS;
Node* copy = AllocateFixedArray(kind, capacity);
CopyFixedArrayElements(kind, fixed_array, kind, copy, capacity, capacity,
UPDATE_WRITE_BARRIER);
result.Bind(copy);
Goto(&done);
}
BIND(&done);
return result.value();
}
Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info, Node* ConstructorBuiltinsAssembler::EmitFastNewClosure(Node* shared_info,
Node* feedback_vector, Node* feedback_vector,
Node* slot, Node* slot,
...@@ -598,14 +628,14 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject( ...@@ -598,14 +628,14 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
VARIABLE(var_properties, MachineRepresentation::kTagged); VARIABLE(var_properties, MachineRepresentation::kTagged);
{ {
// Directly copy over the property store for dict-mode boilerplates. // Directly copy over the property store for dict-mode boilerplates.
Label if_dictionary(this), if_fast(this), allocate_object(this); Label if_dictionary(this), if_fast(this), done(this);
Branch(IsDictionaryMap(boilerplate_map), &if_dictionary, &if_fast); Branch(IsDictionaryMap(boilerplate_map), &if_dictionary, &if_fast);
BIND(&if_dictionary); BIND(&if_dictionary);
{ {
var_properties.Bind( var_properties.Bind(
CopyNameDictionary(LoadProperties(boilerplate), call_runtime)); CopyNameDictionary(LoadProperties(boilerplate), call_runtime));
// Slow objects have no in-object properties. // Slow objects have no in-object properties.
Goto(&allocate_object); Goto(&done);
} }
BIND(&if_fast); BIND(&if_fast);
{ {
...@@ -613,9 +643,29 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject( ...@@ -613,9 +643,29 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
Node* boilerplate_properties = LoadProperties(boilerplate); Node* boilerplate_properties = LoadProperties(boilerplate);
GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime); GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime);
var_properties.Bind(EmptyFixedArrayConstant()); var_properties.Bind(EmptyFixedArrayConstant());
Goto(&allocate_object); Goto(&done);
} }
BIND(&allocate_object); BIND(&done);
}
VARIABLE(var_elements, MachineRepresentation::kTagged);
{
// Copy the elements backing store, assuming that it's flat.
Label if_empty_fixed_array(this), if_copy_elements(this), done(this);
Node* boilerplate_elements = LoadElements(boilerplate);
Branch(IsEmptyFixedArray(boilerplate_elements), &if_empty_fixed_array,
&if_copy_elements);
BIND(&if_empty_fixed_array);
var_elements.Bind(boilerplate_elements);
Goto(&done);
BIND(&if_copy_elements);
CSA_ASSERT(this,
Word32Not(IsFixedCOWArrayMap(LoadMap(boilerplate_elements))));
var_elements.Bind(CopyFixedArrayBase(boilerplate_elements));
Goto(&done);
BIND(&done);
} }
Node* instance_size = TimesPointerSize(LoadMapInstanceSize(boilerplate_map)); Node* instance_size = TimesPointerSize(LoadMapInstanceSize(boilerplate_map));
...@@ -632,10 +682,8 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject( ...@@ -632,10 +682,8 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
StoreMapNoWriteBarrier(copy, boilerplate_map); StoreMapNoWriteBarrier(copy, boilerplate_map);
StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOffset, StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOffset,
var_properties.value()); var_properties.value());
// TODO(cbruni): support elements cloning for object literals.
CSA_ASSERT(this, IsEmptyFixedArray(LoadElements(boilerplate)));
StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset, StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset,
EmptyFixedArrayConstant()); var_elements.value());
} }
// Copy over in-object properties. // Copy over in-object properties.
......
...@@ -41,6 +41,7 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler { ...@@ -41,6 +41,7 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
Node* NonEmptyShallowClone(Node* boilerplate, Node* boilerplate_map, Node* NonEmptyShallowClone(Node* boilerplate, Node* boilerplate_map,
Node* boilerplate_elements, Node* allocation_site, Node* boilerplate_elements, Node* allocation_site,
Node* capacity, ElementsKind kind); Node* capacity, ElementsKind kind);
Node* CopyFixedArrayBase(Node* elements);
}; };
} // namespace internal } // namespace internal
......
...@@ -2276,6 +2276,7 @@ Node* CodeStubAssembler::AllocateFixedArray(ElementsKind kind, ...@@ -2276,6 +2276,7 @@ Node* CodeStubAssembler::AllocateFixedArray(ElementsKind kind,
IntPtrOrSmiConstant(0, mode), mode)); IntPtrOrSmiConstant(0, mode), mode));
Node* total_size = GetFixedArrayAllocationSize(capacity_node, kind, mode); Node* total_size = GetFixedArrayAllocationSize(capacity_node, kind, mode);
if (IsFastDoubleElementsKind(kind)) flags |= kDoubleAlignment;
// Allocate both array and elements object, and initialize the JSArray. // Allocate both array and elements object, and initialize the JSArray.
Node* array = Allocate(total_size, flags); Node* array = Allocate(total_size, flags);
Heap::RootListIndex map_index = IsFastDoubleElementsKind(kind) Heap::RootListIndex map_index = IsFastDoubleElementsKind(kind)
......
...@@ -26,6 +26,9 @@ ...@@ -26,6 +26,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test that we can create object literals of various sizes. // Test that we can create object literals of various sizes.
// Flags: --allow-natives-syntax
function testLiteral(size) { function testLiteral(size) {
// Build object-literal string. // Build object-literal string.
...@@ -45,6 +48,25 @@ function testLiteral(size) { ...@@ -45,6 +48,25 @@ function testLiteral(size) {
assertEquals(i, o["a"+i]); assertEquals(i, o["a"+i]);
} }
} }
function testElementLiteral(size) {
// Build object-literal string.
var literal = "var o = { ";
for (var i = 0; i < size; i++) {
if (i > 0) literal += ",";
literal += (i + ":'" + i+"'");
}
literal += "}";
// Create the object literal.
eval(literal);
// Check that the properties have the expected values.
for (var i = 0; i < size; i++) {
assertEquals(i+"", o[i]);
}
}
// The sizes to test. // The sizes to test.
var sizes = [0, 1, 2, 100, 200, 400, 1000]; var sizes = [0, 1, 2, 100, 200, 400, 1000];
...@@ -52,4 +74,91 @@ var sizes = [0, 1, 2, 100, 200, 400, 1000]; ...@@ -52,4 +74,91 @@ var sizes = [0, 1, 2, 100, 200, 400, 1000];
// Run the test. // Run the test.
for (var i = 0; i < sizes.length; i++) { for (var i = 0; i < sizes.length; i++) {
testLiteral(sizes[i]); testLiteral(sizes[i]);
testElementLiteral(sizes[i]);
}
// Larg Object dictionary mode literal.
function TestSlowLiteralOptimized() {
function f() {
return {__proto__:null, bar:"barValue"};
}
let obj = f();
assertFalse(%HasFastProperties(obj));
assertEquals(Object.getPrototypeOf(obj), null);
assertEquals(["bar"], Object.keys(obj));
assertEquals("barValue", obj.bar);
obj.bar = "barValue2";
assertEquals("barValue2", obj.bar);
%OptimizeFunctionOnNextCall(f);
obj = f();
assertFalse(%HasFastProperties(obj));
assertEquals(Object.getPrototypeOf(obj), null);
assertEquals(["bar"], Object.keys(obj));
assertEquals("barValue", obj.bar);
obj.bar = "barValue2";
assertEquals("barValue2", obj.bar);
};
TestSlowLiteralOptimized();
TestSlowLiteralOptimized();
(function TestLargeObjectDictionaryLiteral() {
// Create potential large-space object literal.
let properties = [];
// Constant chosen so the dictionary properties store lies in large object
// space.
const max = 0x1ef3e / 3;
for (let i = 0; i < max; i++) {
properties.push("p"+i);
}
let source = "return { __proto__:null, " + properties.join(":'',") + ":''}"
let createObject = new Function(source);
let object = createObject();
%HeapObjectVerify(object);
assertFalse(%HasFastProperties(object));
assertEquals(Object.getPrototypeOf(object ), null);
let keys = Object.keys(object);
// modify original object
object['new_property'] = {};
object[1] = 12;
%HeapObjectVerify(object);
let object2 = createObject();
%HeapObjectVerify(object2);
assertFalse(object2 === object);
assertFalse(%HasFastProperties(object2));
assertEquals(Object.getPrototypeOf(object2), null);
assertEquals(keys, Object.keys(object2));
})();
// Test Object literal with large-object elements.
let indices = [];
const max = 0x1ef3e + 100;
for (let i = 0; i < max; i++) {
indices.push(""+i);
} }
let source = "return {" + indices.join(":0,") + ":0};"
let largeElementsLiteral = new Function(source);
function TestLargeObjectElements() {
// Corresponds to FixedArray::kMaxRegularLength.
let object = largeElementsLiteral();
%HeapObjectVerify(object);
for (let i = 0; i < max; i++) {
assertEquals(0, object[i]);
}
object[0] = 0xFF;
assertEquals(0xFF, object[0]);
object[1] = 1.23;
assertEquals(1.23, object[1]);
%HeapObjectVerify(object);
}
TestLargeObjectElements();
TestLargeObjectElements();
TestLargeObjectElements();
%OptimizeFunctionOnNextCall(TestLargeObjectElements);
TestLargeObjectElements();
...@@ -81,7 +81,7 @@ function testSparseElements() { ...@@ -81,7 +81,7 @@ function testSparseElements() {
'0': { x: 12, y: 24 }, '0': { x: 12, y: 24 },
'1000000': { x: 1, y: 2 } '1000000': { x: 1, y: 2 }
}; };
%HeapObjectVerify(sa1);
assertEquals(['0', '1000000'], Object.keys(sa1)); assertEquals(['0', '1000000'], Object.keys(sa1));
assertEquals(12, sa1[0].x); assertEquals(12, sa1[0].x);
assertEquals(24, sa1[0].y); assertEquals(24, sa1[0].y);
...@@ -252,13 +252,15 @@ function TestNumericNames() { ...@@ -252,13 +252,15 @@ function TestNumericNames() {
7E-0: 7, 7E-0: 7,
0x8: 8, 0x8: 8,
0X9: 9, 0X9: 9,
} };
%HeapObjectVerify(o);
assertEquals(['1', '2', '3', '4', '5', '6', '7', '8', '9'], Object.keys(o)); assertEquals(['1', '2', '3', '4', '5', '6', '7', '8', '9'], Object.keys(o));
o = { o = {
1.2: 1.2, 1.2: 1.2,
1.30: 1.3 1.30: 1.3
}; };
%HeapObjectVerify(o);
assertEquals(['1.2', '1.3'], Object.keys(o)); assertEquals(['1.2', '1.3'], Object.keys(o));
} }
TestNumericNames(); TestNumericNames();
...@@ -271,7 +273,14 @@ function TestNonNumberElementValues() { ...@@ -271,7 +273,14 @@ function TestNonNumberElementValues() {
3: undefined, 3: undefined,
4: "" 4: ""
}; };
%HeapObjectVerify(o);
assertEquals(['1', '2', '3', '4'], Object.keys(o)); assertEquals(['1', '2', '3', '4'], Object.keys(o));
assertEquals([true, false, undefined, ""], Object.values(o));
o[1] = 'a';
o[2] = 'b';
assertEquals(['1', '2', '3', '4'], Object.keys(o));
assertEquals(['a', 'b', undefined, ""], Object.values(o));
var o2 = { var o2 = {
1: true, 1: true,
2: false, 2: false,
...@@ -280,7 +289,14 @@ function TestNonNumberElementValues() { ...@@ -280,7 +289,14 @@ function TestNonNumberElementValues() {
a: 'a', a: 'a',
b: 'b' b: 'b'
}; };
%HeapObjectVerify(o2);
assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o2)); assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o2));
assertEquals([true, false, undefined, "", 'a', 'b'], Object.values(o2));
o2[1] = 'a';
o2[2] = 'b';
assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o2));
assertEquals(['a', 'b', undefined, "", 'a', 'b'], Object.values(o2));
var o3 = { var o3 = {
__proto__:null, __proto__:null,
1: true, 1: true,
...@@ -288,7 +304,9 @@ function TestNonNumberElementValues() { ...@@ -288,7 +304,9 @@ function TestNonNumberElementValues() {
3: undefined, 3: undefined,
4: "" 4: ""
}; };
%HeapObjectVerify(o3);
assertEquals(['1', '2', '3', '4'], Object.keys(o3)); assertEquals(['1', '2', '3', '4'], Object.keys(o3));
var o4 = { var o4 = {
__proto__:null, __proto__:null,
1: true, 1: true,
...@@ -298,10 +316,12 @@ function TestNonNumberElementValues() { ...@@ -298,10 +316,12 @@ function TestNonNumberElementValues() {
a: 'a', a: 'a',
b: 'b' b: 'b'
}; };
%HeapObjectVerify(o4);
assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o4)); assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o4));
} }
TestNonNumberElementValues(); TestNonNumberElementValues();
TestNonNumberElementValues(); TestNonNumberElementValues();
TestNonNumberElementValues();
%OptimizeFunctionOnNextCall(TestNonNumberElementValues); %OptimizeFunctionOnNextCall(TestNonNumberElementValues);
TestNonNumberElementValues(); TestNonNumberElementValues();
...@@ -1464,16 +1484,19 @@ TestSlowLiteralOptimized(); ...@@ -1464,16 +1484,19 @@ TestSlowLiteralOptimized();
} }
} }
let object = createObject(); let object = createObject();
assertFalse(%HasFastProperties(object )); %HeapObjectVerify(object);
assertFalse(%HasFastProperties(object));
assertEquals(Object.getPrototypeOf(object ), null); assertEquals(Object.getPrototypeOf(object ), null);
let keys = Object.keys(object); let keys = Object.keys(object);
// modify original object // modify original object
object['new_property'] = {}; object['new_property'] = {};
object[1] = 12; object[1] = 12;
%HeapObjectVerify(object);
let object2 = createObject(); let object2 = createObject();
assertFalse(object === object2 ); %HeapObjectVerify(object2);
assertFalse(%HasFastProperties(object2 )); assertFalse(object2 === object);
assertFalse(%HasFastProperties(object2));
assertEquals(Object.getPrototypeOf(object2), null); assertEquals(Object.getPrototypeOf(object2), null);
assertEquals(keys, Object.keys(object2)); assertEquals(keys, Object.keys(object2));
})(); })();
......
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