Commit c5c50e18 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Port WeakMap/WeakSet constructor to CSA

- Remove weak-collection.js
- Adds TFJ builtins for WeakSet and WeakMap constructors
- Unified helpers and constructor behavior into a BaseCollectionsAssembler
- Fast paths for...
  - unmodified constructor function
  - argument is a fast JS array
  - entries are fast JS arrays, for Map/WeakMap
  - no arguments passed

Quick benchmarks shows significant improvements (1.12x - 5.7x!) for ALL collection constructors (weak and non-weak):
https://github.com/peterwmwong/v8-perf/blob/master/weakcollection-constructor/README.md

More could be done for performance.  Currently we always call out to JS to add entries, if we knew the prototype was unmodified, we could call the builtins directly.

Bug: v8:5049, v8:6604
Change-Id: Id7912c1eed5bcf512df7fd6238f04166a8a5937e
Reviewed-on: https://chromium-review.googlesource.com/760385Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49343}
parent 87aeb5eb
...@@ -569,7 +569,6 @@ action("js2c") { ...@@ -569,7 +569,6 @@ action("js2c") {
"src/js/v8natives.js", "src/js/v8natives.js",
"src/js/array.js", "src/js/array.js",
"src/js/typedarray.js", "src/js/typedarray.js",
"src/js/weak-collection.js",
"src/js/messages.js", "src/js/messages.js",
"src/js/spread.js", "src/js/spread.js",
"src/js/proxy.js", "src/js/proxy.js",
......
...@@ -3300,11 +3300,18 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -3300,11 +3300,18 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
} }
{ // -- W e a k M a p { // -- W e a k M a p
Handle<JSFunction> cons = Handle<JSFunction> cons = InstallFunction(
InstallFunction(global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, global, "WeakMap", JS_WEAK_MAP_TYPE, JSWeakMap::kSize, 0,
0, factory->the_hole_value(), Builtins::kIllegal); factory->the_hole_value(), Builtins::kWeakMapConstructor);
InstallWithIntrinsicDefaultProto(isolate, cons, InstallWithIntrinsicDefaultProto(isolate, cons,
Context::JS_WEAK_MAP_FUN_INDEX); Context::JS_WEAK_MAP_FUN_INDEX);
Handle<SharedFunctionInfo> shared(cons->shared(), isolate);
shared->SetConstructStub(*BUILTIN_CODE(isolate, JSBuiltinsConstructStub));
shared->set_instance_class_name(isolate->heap()->WeakMap_string());
shared->DontAdaptArguments();
shared->set_length(0);
// Setup %WeakMapPrototype%. // Setup %WeakMapPrototype%.
Handle<JSObject> prototype(JSObject::cast(cons->instance_prototype())); Handle<JSObject> prototype(JSObject::cast(cons->instance_prototype()));
...@@ -3322,11 +3329,18 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -3322,11 +3329,18 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
} }
{ // -- W e a k S e t { // -- W e a k S e t
Handle<JSFunction> cons = Handle<JSFunction> cons = InstallFunction(
InstallFunction(global, "WeakSet", JS_WEAK_SET_TYPE, JSWeakSet::kSize, global, "WeakSet", JS_WEAK_SET_TYPE, JSWeakSet::kSize, 0,
0, factory->the_hole_value(), Builtins::kIllegal); factory->the_hole_value(), Builtins::kWeakSetConstructor);
InstallWithIntrinsicDefaultProto(isolate, cons, InstallWithIntrinsicDefaultProto(isolate, cons,
Context::JS_WEAK_SET_FUN_INDEX); Context::JS_WEAK_SET_FUN_INDEX);
Handle<SharedFunctionInfo> shared(cons->shared(), isolate);
shared->SetConstructStub(*BUILTIN_CODE(isolate, JSBuiltinsConstructStub));
shared->set_instance_class_name(isolate->heap()->WeakSet_string());
shared->DontAdaptArguments();
shared->set_length(0);
// Setup %WeakSetPrototype%. // Setup %WeakSetPrototype%.
Handle<JSObject> prototype(JSObject::cast(cons->instance_prototype())); Handle<JSObject> prototype(JSObject::cast(cons->instance_prototype()));
......
This diff is collapsed.
...@@ -1084,6 +1084,7 @@ namespace internal { ...@@ -1084,6 +1084,7 @@ namespace internal {
TFC(ThrowWasmTrapFuncSigMismatch, WasmRuntimeCall, 1) \ TFC(ThrowWasmTrapFuncSigMismatch, WasmRuntimeCall, 1) \
\ \
/* WeakMap */ \ /* WeakMap */ \
TFJ(WeakMapConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFS(WeakMapLookupHashIndex, kTable, kKey) \ TFS(WeakMapLookupHashIndex, kTable, kKey) \
TFJ(WeakMapGet, 1, kKey) \ TFJ(WeakMapGet, 1, kKey) \
TFJ(WeakMapHas, 1, kKey) \ TFJ(WeakMapHas, 1, kKey) \
...@@ -1091,6 +1092,7 @@ namespace internal { ...@@ -1091,6 +1092,7 @@ namespace internal {
TFJ(WeakMapPrototypeDelete, 1, kKey) \ TFJ(WeakMapPrototypeDelete, 1, kKey) \
\ \
/* WeakSet */ \ /* WeakSet */ \
TFJ(WeakSetConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFJ(WeakSetHas, 1, kKey) \ TFJ(WeakSetHas, 1, kKey) \
TFJ(WeakSetPrototypeAdd, 1, kValue) \ TFJ(WeakSetPrototypeAdd, 1, kValue) \
TFJ(WeakSetPrototypeDelete, 1, kValue) \ TFJ(WeakSetPrototypeDelete, 1, kValue) \
......
...@@ -851,6 +851,25 @@ void CodeStubAssembler::BranchIfJSObject(Node* object, Label* if_true, ...@@ -851,6 +851,25 @@ void CodeStubAssembler::BranchIfJSObject(Node* object, Label* if_true,
Branch(IsJSObject(object), if_true, if_false); Branch(IsJSObject(object), if_true, if_false);
} }
TNode<BoolT> CodeStubAssembler::IsFastJSArray(SloppyTNode<Object> object,
SloppyTNode<Context> context) {
Label if_true(this), if_false(this, Label::kDeferred), exit(this);
BranchIfFastJSArray(object, context, &if_true, &if_false);
TVARIABLE(BoolT, var_result);
BIND(&if_true);
{
var_result = ReinterpretCast<BoolT>(Int32Constant(1));
Goto(&exit);
}
BIND(&if_false);
{
var_result = ReinterpretCast<BoolT>(Int32Constant(0));
Goto(&exit);
}
BIND(&exit);
return var_result;
}
void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context, void CodeStubAssembler::BranchIfFastJSArray(Node* object, Node* context,
Label* if_true, Label* if_false) { Label* if_true, Label* if_false) {
// Bailout if receiver is a Smi. // Bailout if receiver is a Smi.
...@@ -6034,8 +6053,8 @@ template Node* CodeStubAssembler::EntryToIndex<NumberDictionary>(Node*, int); ...@@ -6034,8 +6053,8 @@ template Node* CodeStubAssembler::EntryToIndex<NumberDictionary>(Node*, int);
// This must be kept in sync with HashTableBase::ComputeCapacity(). // This must be kept in sync with HashTableBase::ComputeCapacity().
TNode<IntPtrT> CodeStubAssembler::HashTableComputeCapacity( TNode<IntPtrT> CodeStubAssembler::HashTableComputeCapacity(
SloppyTNode<IntPtrT> at_least_space_for) { SloppyTNode<IntPtrT> at_least_space_for) {
Node* capacity = IntPtrRoundUpToPowerOfTwo32(IntPtrAdd( Node* capacity = IntPtrRoundUpToPowerOfTwo32(
at_least_space_for, WordShr(at_least_space_for, IntPtrConstant(1)))); IntPtrAdd(at_least_space_for, WordShr(at_least_space_for, 1)));
return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity)); return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity));
} }
...@@ -10521,6 +10540,11 @@ Node* CodeStubAssembler::IsFastElementsKind(Node* elements_kind) { ...@@ -10521,6 +10540,11 @@ Node* CodeStubAssembler::IsFastElementsKind(Node* elements_kind) {
Int32Constant(LAST_FAST_ELEMENTS_KIND)); Int32Constant(LAST_FAST_ELEMENTS_KIND));
} }
Node* CodeStubAssembler::IsFastSmiOrTaggedElementsKind(Node* elements_kind) {
return Uint32LessThanOrEqual(elements_kind,
Int32Constant(TERMINAL_FAST_ELEMENTS_KIND));
}
Node* CodeStubAssembler::IsHoleyFastElementsKind(Node* elements_kind) { Node* CodeStubAssembler::IsHoleyFastElementsKind(Node* elements_kind) {
CSA_ASSERT(this, IsFastElementsKind(elements_kind)); CSA_ASSERT(this, IsFastElementsKind(elements_kind));
......
...@@ -1043,6 +1043,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1043,6 +1043,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsDictionary(Node* object); Node* IsDictionary(Node* object);
Node* IsExtensibleMap(Node* map); Node* IsExtensibleMap(Node* map);
Node* IsExternalStringInstanceType(Node* instance_type); Node* IsExternalStringInstanceType(Node* instance_type);
TNode<BoolT> IsFastJSArray(SloppyTNode<Object> object,
SloppyTNode<Context> context);
Node* IsFeedbackVector(Node* object); Node* IsFeedbackVector(Node* object);
Node* IsFixedArray(Node* object); Node* IsFixedArray(Node* object);
Node* IsFixedArrayWithKind(Node* object, ElementsKind kind); Node* IsFixedArrayWithKind(Node* object, ElementsKind kind);
...@@ -1119,6 +1121,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1119,6 +1121,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// ElementsKind helpers: // ElementsKind helpers:
Node* IsFastElementsKind(Node* elements_kind); Node* IsFastElementsKind(Node* elements_kind);
Node* IsFastSmiOrTaggedElementsKind(Node* elements_kind);
Node* IsHoleyFastElementsKind(Node* elements_kind); Node* IsHoleyFastElementsKind(Node* elements_kind);
Node* IsElementsKindGreaterThan(Node* target_kind, Node* IsElementsKindGreaterThan(Node* target_kind,
ElementsKind reference_kind); ElementsKind reference_kind);
......
// Copyright 2012 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.
(function(global, utils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
var GlobalWeakMap = global.WeakMap;
var GlobalWeakSet = global.WeakSet;
var MathRandom = global.Math.random;
// -------------------------------------------------------------------
// Harmony WeakMap
function WeakMapConstructor(iterable) {
if (IS_UNDEFINED(new.target)) {
throw %make_type_error(kConstructorNotFunction, "WeakMap");
}
%WeakCollectionInitialize(this);
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.set;
if (!IS_CALLABLE(adder)) {
throw %make_type_error(kPropertyNotFunction, adder, 'set', this);
}
for (var nextItem of iterable) {
if (!IS_RECEIVER(nextItem)) {
throw %make_type_error(kIteratorValueNotAnObject, nextItem);
}
%_Call(adder, this, nextItem[0], nextItem[1]);
}
}
}
// -------------------------------------------------------------------
%SetCode(GlobalWeakMap, WeakMapConstructor);
%FunctionSetLength(GlobalWeakMap, 0);
// -------------------------------------------------------------------
// Harmony WeakSet
function WeakSetConstructor(iterable) {
if (IS_UNDEFINED(new.target)) {
throw %make_type_error(kConstructorNotFunction, "WeakSet");
}
%WeakCollectionInitialize(this);
if (!IS_NULL_OR_UNDEFINED(iterable)) {
var adder = this.add;
if (!IS_CALLABLE(adder)) {
throw %make_type_error(kPropertyNotFunction, adder, 'add', this);
}
for (var value of iterable) {
%_Call(adder, this, value);
}
}
}
// -------------------------------------------------------------------
%SetCode(GlobalWeakSet, WeakSetConstructor);
%FunctionSetLength(GlobalWeakSet, 0);
})
...@@ -2326,7 +2326,6 @@ ...@@ -2326,7 +2326,6 @@
'js/v8natives.js', 'js/v8natives.js',
'js/array.js', 'js/array.js',
'js/typedarray.js', 'js/typedarray.js',
'js/weak-collection.js',
'js/messages.js', 'js/messages.js',
'js/spread.js', 'js/spread.js',
'js/proxy.js', 'js/proxy.js',
......
...@@ -123,10 +123,26 @@ test(function() { ...@@ -123,10 +123,26 @@ test(function() {
}, "Converting circular structure to JSON", TypeError); }, "Converting circular structure to JSON", TypeError);
// kConstructorNotFunction // kConstructorNotFunction
test(function() {
Map();
}, "Constructor Map requires 'new'", TypeError);
test(function() {
Set();
}, "Constructor Set requires 'new'", TypeError);
test(function() { test(function() {
Uint16Array(1); Uint16Array(1);
}, "Constructor Uint16Array requires 'new'", TypeError); }, "Constructor Uint16Array requires 'new'", TypeError);
test(function() {
WeakSet();
}, "Constructor WeakSet requires 'new'", TypeError);
test(function() {
WeakMap();
}, "Constructor WeakMap requires 'new'", TypeError);
// kDataViewNotArrayBuffer // kDataViewNotArrayBuffer
test(function() { test(function() {
new DataView(1); new DataView(1);
...@@ -326,11 +342,26 @@ test(function() { ...@@ -326,11 +342,26 @@ test(function() {
}, "Property description must be an object: 1", TypeError); }, "Property description must be an object: 1", TypeError);
// kPropertyNotFunction // kPropertyNotFunction
test(function() {
Map.prototype.set = 0;
new Map([[1, 2]]);
}, "'0' returned for property 'set' of object '#<Map>' is not a function", TypeError);
test(function() { test(function() {
Set.prototype.add = 0; Set.prototype.add = 0;
new Set(1); new Set([1]);
}, "'0' returned for property 'add' of object '#<Set>' is not a function", TypeError); }, "'0' returned for property 'add' of object '#<Set>' is not a function", TypeError);
test(function() {
WeakMap.prototype.set = 0;
new WeakMap([[{}, 1]]);
}, "'0' returned for property 'set' of object '#<WeakMap>' is not a function", TypeError);
test(function() {
WeakSet.prototype.add = 0;
new WeakSet([{}]);
}, "'0' returned for property 'add' of object '#<WeakSet>' is not a function", TypeError);
// kProtoObjectOrNull // kProtoObjectOrNull
test(function() { test(function() {
Object.setPrototypeOf({}, 1); Object.setPrototypeOf({}, 1);
......
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