Commit 9c5053bf authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Re-enable Set and WeakSet constructor fast path.

- Add Map, WeakMap, Set, and WeakSet initial prototype maps to native context.
- Set and WeakSet constructors check whether prototype map differs from initial
  before choosing the fast path.

Bug: chromium:798026
Change-Id: I5f9cc2463f89e17f06a66b565c625fce133d01fb
Reviewed-on: https://chromium-review.googlesource.com/853698
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50691}
parent b3970791
......@@ -3265,6 +3265,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
BuiltinFunctionId::kMapSize);
SimpleInstallFunction(prototype, "values", Builtins::kMapPrototypeValues, 0,
true);
native_context()->set_initial_map_prototype_map(prototype->map());
InstallSpeciesGetter(js_map_fun);
}
......@@ -3316,6 +3319,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
JSObject::AddProperty(prototype, factory->keys_string(), values, DONT_ENUM);
JSObject::AddProperty(prototype, factory->iterator_symbol(), values,
DONT_ENUM);
native_context()->set_initial_set_prototype_map(prototype->map());
InstallSpeciesGetter(js_set_fun);
}
......@@ -3382,13 +3388,16 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kWeakMapPrototypeDelete, 1, true);
SimpleInstallFunction(prototype, "get", Builtins::kWeakMapGet, 1, true);
SimpleInstallFunction(prototype, "has", Builtins::kWeakMapHas, 1, true);
SimpleInstallFunction(prototype, "set", Builtins::kWeakMapPrototypeSet, 2,
true);
Handle<JSFunction> weakmap_set = SimpleInstallFunction(
prototype, "set", Builtins::kWeakMapPrototypeSet, 2, true);
native_context()->set_weakmap_set(*weakmap_set);
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("WeakMap"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
native_context()->set_initial_weakmap_prototype_map(prototype->map());
}
{ // -- W e a k S e t
......@@ -3410,13 +3419,16 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(prototype, "delete",
Builtins::kWeakSetPrototypeDelete, 1, true);
SimpleInstallFunction(prototype, "has", Builtins::kWeakSetHas, 1, true);
SimpleInstallFunction(prototype, "add", Builtins::kWeakSetPrototypeAdd, 1,
true);
Handle<JSFunction> weakset_add = SimpleInstallFunction(
prototype, "add", Builtins::kWeakSetPrototypeAdd, 1, true);
native_context()->set_weakset_add(*weakset_add);
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("WeakSet"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
native_context()->set_initial_weakset_prototype_map(prototype->map());
}
{ // -- P r o x y
......
......@@ -289,12 +289,8 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread(
&if_runtime);
// Check that the map of the initial array iterator hasn't changed.
Node* native_context = LoadNativeContext(context);
Node* arr_it_proto_map = LoadMap(CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)));
Node* initial_map = LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX);
GotoIfNot(WordEqual(arr_it_proto_map, initial_map), &if_runtime);
TNode<Context> native_context = LoadNativeContext(context);
GotoIfNot(HasInitialArrayIteratorPrototypeMap(native_context), &if_runtime);
Node* kind = LoadMapElementsKind(spread_map);
......
This diff is collapsed.
......@@ -1265,6 +1265,15 @@ Node* CodeStubAssembler::HasInstanceType(Node* object,
return InstanceTypeEqual(LoadInstanceType(object), instance_type);
}
TNode<BoolT> CodeStubAssembler::HasInitialArrayIteratorPrototypeMap(
TNode<Context> native_context) {
TNode<Map> arr_it_proto_map = LoadMap(CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)));
TNode<Map> initial_map = CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX));
return WordEqual(arr_it_proto_map, initial_map);
}
Node* CodeStubAssembler::DoesntHaveInstanceType(Node* object,
InstanceType instance_type) {
return Word32NotEqual(LoadInstanceType(object), Int32Constant(instance_type));
......
......@@ -497,6 +497,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<Int32T> LoadInstanceType(SloppyTNode<HeapObject> object);
// Compare the instance the type of the object against the provided one.
Node* HasInstanceType(Node* object, InstanceType type);
// Determines whether the Array Iterator's prototype has changed.
TNode<BoolT> HasInitialArrayIteratorPrototypeMap(
TNode<Context> native_context);
Node* DoesntHaveInstanceType(Node* object, InstanceType type);
Node* TaggedDoesntHaveInstanceType(Node* any_tagged, InstanceType type);
// Load the properties backing store of a JSObject.
......
......@@ -121,7 +121,10 @@ enum ContextLookupFlags {
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_compile_error_function) \
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, wasm_runtime_error_function)
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_runtime_error_function) \
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add)
#define NATIVE_CONTEXT_JS_ARRAY_ITERATOR_MAPS(V) \
V(TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX, Map, typed_array_key_iterator_map) \
......@@ -266,8 +269,12 @@ enum ContextLookupFlags {
V(INITIAL_ASYNC_GENERATOR_PROTOTYPE_INDEX, JSObject, \
initial_async_generator_prototype) \
V(INITIAL_ITERATOR_PROTOTYPE_INDEX, JSObject, initial_iterator_prototype) \
V(INITIAL_MAP_PROTOTYPE_MAP_INDEX, Map, initial_map_prototype_map) \
V(INITIAL_OBJECT_PROTOTYPE_INDEX, JSObject, initial_object_prototype) \
V(INITIAL_SET_PROTOTYPE_MAP_INDEX, Map, initial_set_prototype_map) \
V(INITIAL_STRING_PROTOTYPE_INDEX, JSObject, initial_string_prototype) \
V(INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX, Map, initial_weakmap_prototype_map) \
V(INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX, Map, initial_weakset_prototype_map) \
V(INT16_ARRAY_FUN_INDEX, JSFunction, int16_array_fun) \
V(INT32_ARRAY_FUN_INDEX, JSFunction, int32_array_fun) \
V(INT8_ARRAY_FUN_INDEX, JSFunction, int8_array_fun) \
......
// Copyright 2018 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 --opt
function TestSetWithModifiedIterator(ctor) {
const k1 = {};
const k2 = {};
const entries = [k1, k2];
const arrayIteratorProto = Object.getPrototypeOf(entries[Symbol.iterator]());
const originalNext = arrayIteratorProto.next;
let callCount = 0;
arrayIteratorProto.next = function() {
callCount++;
return originalNext.call(this);
};
const set = new ctor(entries);
assertEquals(3, callCount); // +1 for iterator done
if('size' in set) assertEquals(2, set.size);
assertTrue(set.has(k1));
assertTrue(set.has(k2));
arrayIteratorProto.next = originalNext;
}
TestSetWithModifiedIterator(Set);
TestSetWithModifiedIterator(Set);
TestSetWithModifiedIterator(Set);
%OptimizeFunctionOnNextCall(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(Set);
assertOptimized(TestSetWithModifiedIterator);
%DeoptimizeFunction(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(WeakSet);
TestSetWithModifiedIterator(WeakSet);
TestSetWithModifiedIterator(WeakSet);
%OptimizeFunctionOnNextCall(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(WeakSet);
assertOptimized(TestSetWithModifiedIterator);
%DeoptimizeFunction(TestSetWithModifiedIterator);
function TestMapWithModifiedIterator(ctor) {
const k1 = {};
const k2 = {};
const entries = [[k1, 1], [k2, 2]];
const arrayIteratorProto = Object.getPrototypeOf(entries[Symbol.iterator]());
const originalNext = arrayIteratorProto.next;
let callCount = 0;
arrayIteratorProto.next = function() {
callCount++;
return originalNext.call(this);
};
const set = new ctor(entries);
assertEquals(3, callCount); // +1 for iterator done
if('size' in set) assertEquals(2, set.size);
assertEquals(1, set.get(k1));
assertEquals(2, set.get(k2));
arrayIteratorProto.next = originalNext;
}
TestMapWithModifiedIterator(Map);
TestMapWithModifiedIterator(Map);
TestMapWithModifiedIterator(Map);
%OptimizeFunctionOnNextCall(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(Map);
assertOptimized(TestMapWithModifiedIterator);
%DeoptimizeFunction(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(WeakMap);
TestMapWithModifiedIterator(WeakMap);
TestMapWithModifiedIterator(WeakMap);
%OptimizeFunctionOnNextCall(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(WeakMap);
assertOptimized(TestMapWithModifiedIterator);
%DeoptimizeFunction(TestMapWithModifiedIterator);
// Copyright 2018 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 --opt
function TestSetPrototypeModified(ctor) {
const originalPrototypeAdd = ctor.prototype.add;
const k1 = {};
const k2 = {};
const entries = [k1, k2];
let addCount = 0;
ctor.prototype.add = function(value) {
addCount++;
originalPrototypeAdd.call(this, value);
entries.length = 1;
};
const set = new ctor(entries);
assertEquals(1, addCount);
assertTrue(set.has(k1));
assertFalse(set.has(k2));
ctor.prototype.add = originalPrototypeAdd;
}
TestSetPrototypeModified(Set);
TestSetPrototypeModified(Set);
TestSetPrototypeModified(Set);
%OptimizeFunctionOnNextCall(TestSetPrototypeModified);
TestSetPrototypeModified(Set);
assertOptimized(TestSetPrototypeModified);
%DeoptimizeFunction(TestSetPrototypeModified);
TestSetPrototypeModified(WeakSet);
TestSetPrototypeModified(WeakSet);
TestSetPrototypeModified(WeakSet);
%OptimizeFunctionOnNextCall(TestSetPrototypeModified);
TestSetPrototypeModified(WeakSet);
assertOptimized(TestSetPrototypeModified);
%DeoptimizeFunction(TestSetPrototypeModified);
function TestMapPrototypeModified(ctor) {
const originalPrototypeSet = ctor.prototype.set;
const k1 = {};
const k2 = {};
const entries = [[k1, 1], [k2, 2]];
let setCount = 0;
ctor.prototype.set = function(key, value) {
setCount++;
originalPrototypeSet.call(this, key, value);
entries.length = 1;
};
const map = new ctor(entries);
assertEquals(1, setCount);
assertTrue(map.has(k1));
assertFalse(map.has(k2));
ctor.prototype.set = originalPrototypeSet;
}
TestMapPrototypeModified(Map);
TestMapPrototypeModified(Map);
TestMapPrototypeModified(Map);
%OptimizeFunctionOnNextCall(TestMapPrototypeModified);
TestMapPrototypeModified(Map);
assertOptimized(TestMapPrototypeModified);
%DeoptimizeFunction(TestMapPrototypeModified);
TestMapPrototypeModified(WeakMap);
TestMapPrototypeModified(WeakMap);
TestMapPrototypeModified(WeakMap);
%OptimizeFunctionOnNextCall(TestMapPrototypeModified);
TestMapPrototypeModified(WeakMap);
assertOptimized(TestMapPrototypeModified);
// Copyright 2018 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 --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
let callCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
input.length = 2;
return k1;
}
}),
[k2, 2],
Object.defineProperty([, 3], "0", {
get() {
callCount++;
return k3;
}
})
];
const col = new ctor(input);
assertEquals(0, callCount);
if ('size' in col) assertEquals(2, col.size);
assertEquals(col.get(k1), 1);
assertEquals(col.get(k2), 2);
assertFalse(col.has(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// Copyright 2018 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 --opt
function TestMapConstructorEntrySideEffect(ctor) {
const originalPrototypeSet = ctor.prototype.set;
const k1 = {};
const k2 = {};
let callCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify continuation retains original set function
ctor.prototype.set = () => {
callCount++;
};
return k1;
}
}),
[k2, 2]
];
const col = new ctor(input);
assertEquals(0, callCount);
if ('size' in col) assertEquals(2, col.size);
assertTrue(col.has(k1));
assertTrue(col.has(k2));
const col2 = new ctor(input);
assertEquals(2, callCount);
if ('size' in col) assertEquals(0, col2.size);
assertFalse(col2.has(k1));
assertFalse(col2.has(k2));
ctor.prototype.set = originalPrototypeSet;
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// Copyright 2018 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 --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify continuation accesses properly accesses subsequent entries
Object.defineProperty(input, "1", {
get: () => [k3, 3]
});
return k1;
}
}),
[k2, 2]
];
const col = new ctor(input);
if ('size' in col) assertEquals(2, col.size);
assertTrue(col.has(k1));
assertFalse(col.has(k2));
assertTrue(col.has(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// Copyright 2018 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 --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
let firstEntryCallCount = 0;
let lastEntryCallCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify handling of a non-Smi array length
input.length = 2 ** 32 - 2;
firstEntryCallCount++;
return k1;
}
}),
[k2, 2],
Object.defineProperty([k3, ], "1", {
get() {
input.length = 1;
lastEntryCallCount++;
return 3;
}
})
];
const col = new ctor(input);
assertEquals(1, firstEntryCallCount,);
assertEquals(1, lastEntryCallCount);
if ('size' in col) assertEquals(3, col.size);
assertEquals(1, col.get(k1));
assertEquals(2, col.get(k2));
assertEquals(3, col.get(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
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