Commit 206b59c7 authored by Shiyu Zhang's avatar Shiyu Zhang Committed by Commit Bot

[runtime] Handle element in for-in fast path that uses PrototypeInfo cache

This can speed up the below micro-bench by 3x and improve JetStream2-tagcloud-SP case by ~2%.

Object.prototype.foo = function() {};
let array = ['a','b','c','d','e'];
let start = Date.now();
for (let i = 0; i < 1e5; i++) {
	for (let j in array) {}
}
console.log(Date.now() - start);

Contributed by tao.pan@intel.com

Change-Id: I44c948c2e4c28b8e42192f36802a5ea0f82bbe25
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2049903Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com>
Cr-Commit-Position: refs/heads/master@{#66362}
parent 4fa721c6
......@@ -57,13 +57,14 @@ static int AddKey(Object key, Handle<FixedArray> combined_keys,
static Handle<FixedArray> CombineKeys(Isolate* isolate,
Handle<FixedArray> own_keys,
Handle<FixedArray> prototype_chain_keys,
Handle<JSReceiver> receiver) {
Handle<JSReceiver> receiver,
bool may_have_elements) {
int prototype_chain_keys_length = prototype_chain_keys->length();
if (prototype_chain_keys_length == 0) return own_keys;
Map map = receiver->map();
int nof_descriptors = map.NumberOfOwnDescriptors();
if (nof_descriptors == 0) return prototype_chain_keys;
if (nof_descriptors == 0 && !may_have_elements) return prototype_chain_keys;
Handle<DescriptorArray> descs(map.instance_descriptors(), isolate);
int own_keys_length = own_keys.is_null() ? 0 : own_keys->length();
......@@ -323,13 +324,18 @@ void FastKeyAccumulator::Prepare() {
// Fully walk the prototype chain and find the last prototype with keys.
is_receiver_simple_enum_ = false;
has_empty_prototype_ = true;
only_own_has_simple_elements_ =
!receiver_->map().IsCustomElementsReceiverMap();
JSReceiver last_prototype;
may_have_elements_ = MayHaveElements(*receiver_);
for (PrototypeIterator iter(isolate_, *receiver_); !iter.IsAtEnd();
iter.Advance()) {
JSReceiver current = iter.GetCurrent<JSReceiver>();
if (!may_have_elements_) {
may_have_elements_ = MayHaveElements(current);
if (!may_have_elements_ || only_own_has_simple_elements_) {
if (MayHaveElements(current)) {
may_have_elements_ = true;
only_own_has_simple_elements_ = false;
}
}
bool has_no_properties = CheckAndInitalizeEmptyEnumCache(current);
if (has_no_properties) continue;
......@@ -564,8 +570,21 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysWithPrototypeInfoCache(
GetKeysConversion keys_conversion) {
Handle<FixedArray> own_keys = KeyAccumulator::GetOwnEnumPropertyKeys(
isolate_, Handle<JSObject>::cast(receiver_));
Handle<FixedArray> own_keys;
if (may_have_elements_) {
if (receiver_->map().is_dictionary_map()) {
GetOwnKeysWithElements<false>(isolate_, Handle<JSObject>::cast(receiver_),
keys_conversion, skip_indices_)
.ToHandle(&own_keys);
} else {
GetOwnKeysWithElements<true>(isolate_, Handle<JSObject>::cast(receiver_),
keys_conversion, skip_indices_)
.ToHandle(&own_keys);
}
} else {
own_keys = KeyAccumulator::GetOwnEnumPropertyKeys(
isolate_, Handle<JSObject>::cast(receiver_));
}
Handle<FixedArray> prototype_chain_keys;
if (has_prototype_info_cache_) {
prototype_chain_keys =
......@@ -586,7 +605,8 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysWithPrototypeInfoCache(
MaybeHandle<FixedArray>());
prototype_chain_keys = accumulator.GetKeys(keys_conversion);
}
return CombineKeys(isolate_, own_keys, prototype_chain_keys, receiver_);
return CombineKeys(isolate_, own_keys, prototype_chain_keys, receiver_,
may_have_elements_);
}
bool FastKeyAccumulator::MayHaveElements(JSReceiver receiver) {
......@@ -598,7 +618,7 @@ bool FastKeyAccumulator::MayHaveElements(JSReceiver receiver) {
}
bool FastKeyAccumulator::TryPrototypeInfoCache(Handle<JSReceiver> receiver) {
if (may_have_elements_) return false;
if (may_have_elements_ && !only_own_has_simple_elements_) return false;
Handle<JSObject> object = Handle<JSObject>::cast(receiver);
if (!object->HasFastProperties()) return false;
if (object->HasNamedInterceptor()) return false;
......
......@@ -192,6 +192,7 @@ class FastKeyAccumulator {
bool may_have_elements_ = true;
bool has_prototype_info_cache_ = false;
bool try_prototype_info_cache_ = false;
bool only_own_has_simple_elements_ = false;
DISALLOW_COPY_AND_ASSIGN(FastKeyAccumulator);
};
......
......@@ -179,7 +179,7 @@ for_in_string_prototype();
}
})();
(function for_in_prototype_change_element() {
(function for_in_prototype_change_element1() {
let prototype1 = {prop: 0, prop1: 1};
let derived1 = {prop2: 2, prop3: 3};
......@@ -199,6 +199,74 @@ for_in_string_prototype();
}
})();
(function for_in_prototype_change_element2() {
Array.prototype.__proto__ = {'A': 1};
let array = ['a', 'b', 'c', 'd', 'e'];
for (let i = 0; i < 3; i++) {
assertEquals(['0', '1', '2', '3', '4', 'A'], Accumulate(array));
}
Array.prototype[10] = 'b';
for (let i = 0; i < 3; i++) {
assertEquals(['0', '1', '2', '3', '4', '10', 'A'], Accumulate(array));
}
})();
(function for_in_prototype_change_element3() {
let prototype = {prop: 0};
let holey_array = {
1: 'a',
get 3() {
delete this[5];
return 'b';
},
5: 'c'
};
Object.setPrototypeOf(holey_array, prototype);
for (let i = 0; i < 3; i++) {
assertEquals(['1', '3', '5', 'prop'], Accumulate(holey_array));
}
prototype[10] = 'b';
for (let i = 0; i < 3; i++) {
assertEquals(['1', '3', '5', '10', 'prop'], Accumulate(holey_array));
}
for (let i = 0; i < 3; i++) {
var accumulator = [];
for (var j in holey_array) {
accumulator.push(j);
holey_array[j];
}
assertEquals(['1', '3', '10', 'prop'], accumulator);
}
})();
(function for_in_prototype_change_element4() {
let prototype = {
1: 'a',
get 3() {
delete this[5];
return 'b';
},
5: 'c',
};
let holey_array = {7: 'd', 9: 'e'};
Object.setPrototypeOf(holey_array, prototype);
for (let i = 0; i < 3; i++) {
assertEquals(['7', '9', '1', '3', '5'], Accumulate(holey_array));
}
prototype.prop = 0;
for (let i = 0; i < 3; i++) {
assertEquals(['7', '9', '1', '3', '5', 'prop'], Accumulate(holey_array));
}
for (let i = 0; i < 3; i++) {
var accumulator = [];
for (var j in holey_array) {
accumulator.push(j);
prototype[j];
}
assertEquals(['7', '9', '1', '3', 'prop'], accumulator);
}
})();
(function for_in_non_enumerable1() {
let prototype1 = {prop: 0};
let derived1 = Object.create(prototype1, {
......
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