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 {
}
bool has_elements() const { return HasElementsField::decode(bit_field_); }
bool has_shallow_properties() const {
return depth() == 1 && !has_elements() && !may_store_doubles();
return depth() == 1 && !may_store_doubles();
}
bool has_rest_property() const {
return HasRestPropertyField::decode(bit_field_);
......
......@@ -28,6 +28,36 @@ void Builtins::Generate_ConstructFunctionForwardVarargs(MacroAssembler* masm) {
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* feedback_vector,
Node* slot,
......@@ -598,14 +628,14 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
VARIABLE(var_properties, MachineRepresentation::kTagged);
{
// 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);
BIND(&if_dictionary);
{
var_properties.Bind(
CopyNameDictionary(LoadProperties(boilerplate), call_runtime));
// Slow objects have no in-object properties.
Goto(&allocate_object);
Goto(&done);
}
BIND(&if_fast);
{
......@@ -613,9 +643,29 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
Node* boilerplate_properties = LoadProperties(boilerplate);
GotoIfNot(IsEmptyFixedArray(boilerplate_properties), call_runtime);
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));
......@@ -632,10 +682,8 @@ Node* ConstructorBuiltinsAssembler::EmitFastCloneShallowObject(
StoreMapNoWriteBarrier(copy, boilerplate_map);
StoreObjectFieldNoWriteBarrier(copy, JSObject::kPropertiesOffset,
var_properties.value());
// TODO(cbruni): support elements cloning for object literals.
CSA_ASSERT(this, IsEmptyFixedArray(LoadElements(boilerplate)));
StoreObjectFieldNoWriteBarrier(copy, JSObject::kElementsOffset,
EmptyFixedArrayConstant());
var_elements.value());
}
// Copy over in-object properties.
......
......@@ -41,6 +41,7 @@ class ConstructorBuiltinsAssembler : public CodeStubAssembler {
Node* NonEmptyShallowClone(Node* boilerplate, Node* boilerplate_map,
Node* boilerplate_elements, Node* allocation_site,
Node* capacity, ElementsKind kind);
Node* CopyFixedArrayBase(Node* elements);
};
} // namespace internal
......
......@@ -2276,6 +2276,7 @@ Node* CodeStubAssembler::AllocateFixedArray(ElementsKind kind,
IntPtrOrSmiConstant(0, mode), mode));
Node* total_size = GetFixedArrayAllocationSize(capacity_node, kind, mode);
if (IsFastDoubleElementsKind(kind)) flags |= kDoubleAlignment;
// Allocate both array and elements object, and initialize the JSArray.
Node* array = Allocate(total_size, flags);
Heap::RootListIndex map_index = IsFastDoubleElementsKind(kind)
......
......@@ -26,6 +26,9 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test that we can create object literals of various sizes.
// Flags: --allow-natives-syntax
function testLiteral(size) {
// Build object-literal string.
......@@ -45,6 +48,25 @@ function testLiteral(size) {
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.
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.
for (var i = 0; i < sizes.length; 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() {
'0': { x: 12, y: 24 },
'1000000': { x: 1, y: 2 }
};
%HeapObjectVerify(sa1);
assertEquals(['0', '1000000'], Object.keys(sa1));
assertEquals(12, sa1[0].x);
assertEquals(24, sa1[0].y);
......@@ -252,13 +252,15 @@ function TestNumericNames() {
7E-0: 7,
0x8: 8,
0X9: 9,
}
};
%HeapObjectVerify(o);
assertEquals(['1', '2', '3', '4', '5', '6', '7', '8', '9'], Object.keys(o));
o = {
1.2: 1.2,
1.30: 1.3
};
%HeapObjectVerify(o);
assertEquals(['1.2', '1.3'], Object.keys(o));
}
TestNumericNames();
......@@ -271,7 +273,14 @@ function TestNonNumberElementValues() {
3: undefined,
4: ""
};
%HeapObjectVerify(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 = {
1: true,
2: false,
......@@ -280,7 +289,14 @@ function TestNonNumberElementValues() {
a: 'a',
b: 'b'
};
%HeapObjectVerify(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 = {
__proto__:null,
1: true,
......@@ -288,7 +304,9 @@ function TestNonNumberElementValues() {
3: undefined,
4: ""
};
%HeapObjectVerify(o3);
assertEquals(['1', '2', '3', '4'], Object.keys(o3));
var o4 = {
__proto__:null,
1: true,
......@@ -298,10 +316,12 @@ function TestNonNumberElementValues() {
a: 'a',
b: 'b'
};
%HeapObjectVerify(o4);
assertEquals(['1', '2', '3', '4', 'a', 'b'], Object.keys(o4));
}
TestNonNumberElementValues();
TestNonNumberElementValues();
TestNonNumberElementValues();
%OptimizeFunctionOnNextCall(TestNonNumberElementValues);
TestNonNumberElementValues();
......@@ -1464,16 +1484,19 @@ TestSlowLiteralOptimized();
}
}
let object = createObject();
assertFalse(%HasFastProperties(object ));
%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();
assertFalse(object === object2 );
assertFalse(%HasFastProperties(object2 ));
%HeapObjectVerify(object2);
assertFalse(object2 === object);
assertFalse(%HasFastProperties(object2));
assertEquals(Object.getPrototypeOf(object2), null);
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