Revert "Reland "[runtime] Cache prototype chain enumerable keys in PrototypeInfo""
This reverts commit3b7535636f
. Reason for revert: breaks in multiple places: https://bugs.chromium.org/p/chromium/issues/detail?id=1029368 https://bugs.chromium.org/p/chromium/issues/detail?id=1029361 Original change's description: > Reland "[runtime] Cache prototype chain enumerable keys in PrototypeInfo" > > This is a reland of5253d7bf15
> > Original change's description: > > [runtime] Cache prototype chain enumerable keys in PrototypeInfo > > > > This CL adds a prototype_chain_enum_cache to cache the enumeration of a > > prototype and its entire chain on the PrototypeInfo. It can improve for-in > > performance via simply merging the receiver enumeration with this cache. > > > > It improves the score of JetStream2-tagcloud-SP case by ~9% on IA Chromebook. > > > > Contributed by tao.pan@intel.com > > > > Change-Id: Ib40bfe41e772672337155584672f06fa1ba1e70d > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1870844 > > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > > Reviewed-by: Toon Verwaest <verwaest@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#65224} > > Change-Id: I93b74727c46abbaab163324c50fbd977fcc9bb36 > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1955232 > Reviewed-by: Toon Verwaest <verwaest@chromium.org> > Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> > Cr-Commit-Position: refs/heads/master@{#65377} TBR=verwaest@chromium.org,shiyu.zhang@intel.com # Not skipping CQ checks because original CL landed > 1 day ago. Change-Id: I5b0d544e802ffda6a6804931087f37cb112805ef Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1962273 Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#65418}
This commit is contained in:
parent
4a9cf942d0
commit
09278f79f2
@ -4402,11 +4402,6 @@ void InvalidateOnePrototypeValidityCellInternal(Map map) {
|
||||
Cell cell = Cell::cast(maybe_cell);
|
||||
cell.set_value(Smi::FromInt(Map::kPrototypeChainInvalid));
|
||||
}
|
||||
Object maybe_prototype_info = map.prototype_info();
|
||||
if (maybe_prototype_info.IsPrototypeInfo()) {
|
||||
PrototypeInfo prototype_info = PrototypeInfo::cast(maybe_prototype_info);
|
||||
prototype_info.set_prototype_chain_enum_cache(Object());
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidatePrototypeChainsInternal(Map map) {
|
||||
|
@ -44,42 +44,6 @@ static bool ContainsOnlyValidKeys(Handle<FixedArray> array) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int AddKey(Object key, Handle<FixedArray> combined_keys,
|
||||
Handle<DescriptorArray> descs, int nof_descriptors,
|
||||
int target) {
|
||||
for (InternalIndex i : InternalIndex::Range(nof_descriptors)) {
|
||||
if (descs->GetKey(i) == key) return 0;
|
||||
}
|
||||
combined_keys->set(target, key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Handle<FixedArray> CombineKeys(Isolate* isolate,
|
||||
Handle<FixedArray> own_keys,
|
||||
Handle<FixedArray> prototype_chain_keys,
|
||||
Handle<JSReceiver> receiver) {
|
||||
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;
|
||||
|
||||
Handle<DescriptorArray> descs(map.instance_descriptors(), isolate);
|
||||
int own_keys_length = own_keys.is_null() ? 0 : own_keys->length();
|
||||
Handle<FixedArray> combined_keys = isolate->factory()->NewFixedArray(
|
||||
own_keys_length + prototype_chain_keys_length);
|
||||
if (own_keys_length != 0) {
|
||||
own_keys->CopyTo(0, *combined_keys, 0, own_keys_length);
|
||||
}
|
||||
int target_keys_length = own_keys_length;
|
||||
for (int i = 0; i < prototype_chain_keys_length; i++) {
|
||||
target_keys_length += AddKey(prototype_chain_keys->get(i), combined_keys,
|
||||
descs, nof_descriptors, target_keys_length);
|
||||
}
|
||||
return FixedArray::ShrinkOrEmpty(isolate, combined_keys, target_keys_length);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@ -104,14 +68,6 @@ Handle<FixedArray> KeyAccumulator::GetKeys(GetKeysConversion convert) {
|
||||
Handle<FixedArray> result =
|
||||
OrderedHashSet::ConvertToKeysArray(isolate(), keys(), convert);
|
||||
DCHECK(ContainsOnlyValidKeys(result));
|
||||
|
||||
if (try_prototype_info_cache_ && !first_prototype_map_.is_null()) {
|
||||
PrototypeInfo::cast(first_prototype_map_->prototype_info())
|
||||
.set_prototype_chain_enum_cache(*result);
|
||||
Map::GetOrCreatePrototypeChainValidityCell(
|
||||
Handle<Map>(receiver_->map(), isolate_), isolate_);
|
||||
DCHECK(first_prototype_map_->IsPrototypeValidityCellValid());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -335,9 +291,6 @@ void FastKeyAccumulator::Prepare() {
|
||||
last_prototype = current;
|
||||
has_empty_prototype_ = false;
|
||||
}
|
||||
// Check if we should try to create/use prototype info cache.
|
||||
try_prototype_info_cache_ = TryPrototypeInfoCache(receiver_);
|
||||
if (has_prototype_info_cache_) return;
|
||||
if (has_empty_prototype_) {
|
||||
is_receiver_simple_enum_ =
|
||||
receiver_->map().EnumLength() != kInvalidEnumCacheSentinel &&
|
||||
@ -479,9 +432,6 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeys(
|
||||
if (isolate_->has_pending_exception()) return MaybeHandle<FixedArray>();
|
||||
}
|
||||
|
||||
if (try_prototype_info_cache_) {
|
||||
return GetKeysWithPrototypeInfoCache(keys_conversion);
|
||||
}
|
||||
return GetKeysSlow(keys_conversion);
|
||||
}
|
||||
|
||||
@ -553,41 +503,12 @@ MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysSlow(
|
||||
accumulator.set_skip_indices(skip_indices_);
|
||||
accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
|
||||
accumulator.set_may_have_elements(may_have_elements_);
|
||||
accumulator.set_first_prototype_map(first_prototype_map_);
|
||||
accumulator.set_try_prototype_info_cache(try_prototype_info_cache_);
|
||||
|
||||
MAYBE_RETURN(accumulator.CollectKeys(receiver_, receiver_),
|
||||
MaybeHandle<FixedArray>());
|
||||
return accumulator.GetKeys(keys_conversion);
|
||||
}
|
||||
|
||||
MaybeHandle<FixedArray> FastKeyAccumulator::GetKeysWithPrototypeInfoCache(
|
||||
GetKeysConversion keys_conversion) {
|
||||
Handle<FixedArray> own_keys = KeyAccumulator::GetOwnEnumPropertyKeys(
|
||||
isolate_, Handle<JSObject>::cast(receiver_));
|
||||
Handle<FixedArray> prototype_chain_keys;
|
||||
if (has_prototype_info_cache_) {
|
||||
prototype_chain_keys =
|
||||
handle(FixedArray::cast(
|
||||
PrototypeInfo::cast(first_prototype_map_->prototype_info())
|
||||
.prototype_chain_enum_cache()),
|
||||
isolate_);
|
||||
} else {
|
||||
KeyAccumulator accumulator(isolate_, mode_, filter_);
|
||||
accumulator.set_is_for_in(is_for_in_);
|
||||
accumulator.set_skip_indices(skip_indices_);
|
||||
accumulator.set_last_non_empty_prototype(last_non_empty_prototype_);
|
||||
accumulator.set_may_have_elements(may_have_elements_);
|
||||
accumulator.set_receiver(receiver_);
|
||||
accumulator.set_first_prototype_map(first_prototype_map_);
|
||||
accumulator.set_try_prototype_info_cache(try_prototype_info_cache_);
|
||||
MAYBE_RETURN(accumulator.CollectKeys(first_prototype_, first_prototype_),
|
||||
MaybeHandle<FixedArray>());
|
||||
prototype_chain_keys = accumulator.GetKeys(keys_conversion);
|
||||
}
|
||||
return CombineKeys(isolate_, own_keys, prototype_chain_keys, receiver_);
|
||||
}
|
||||
|
||||
bool FastKeyAccumulator::MayHaveElements(JSReceiver receiver) {
|
||||
if (!receiver.IsJSObject()) return true;
|
||||
JSObject object = JSObject::cast(receiver);
|
||||
@ -596,28 +517,6 @@ bool FastKeyAccumulator::MayHaveElements(JSReceiver receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FastKeyAccumulator::TryPrototypeInfoCache(Handle<JSReceiver> receiver) {
|
||||
if (may_have_elements_) return false;
|
||||
Handle<JSObject> object = Handle<JSObject>::cast(receiver);
|
||||
if (!object->HasFastProperties()) return false;
|
||||
if (object->HasNamedInterceptor()) return false;
|
||||
if (object->IsAccessCheckNeeded() &&
|
||||
!isolate_->MayAccess(handle(isolate_->context(), isolate_), object)) {
|
||||
return false;
|
||||
}
|
||||
HeapObject prototype = receiver->map().prototype();
|
||||
if (prototype.is_null()) return false;
|
||||
if (!prototype.map().is_prototype_map()) return false;
|
||||
first_prototype_ = handle(JSReceiver::cast(prototype), isolate_);
|
||||
Handle<Map> map(prototype.map(), isolate_);
|
||||
first_prototype_map_ = map;
|
||||
has_prototype_info_cache_ = map->IsPrototypeValidityCellValid() &&
|
||||
PrototypeInfo::cast(map->prototype_info())
|
||||
.prototype_chain_enum_cache()
|
||||
.IsFixedArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum IndexedOrNamed { kIndexed, kNamed };
|
||||
|
@ -87,13 +87,6 @@ class KeyAccumulator final {
|
||||
// indices.
|
||||
void set_is_for_in(bool value) { is_for_in_ = value; }
|
||||
void set_skip_indices(bool value) { skip_indices_ = value; }
|
||||
void set_first_prototype_map(Handle<Map> value) {
|
||||
first_prototype_map_ = value;
|
||||
}
|
||||
void set_try_prototype_info_cache(bool value) {
|
||||
try_prototype_info_cache_ = value;
|
||||
}
|
||||
void set_receiver(Handle<JSReceiver> object) { receiver_ = object; }
|
||||
// The last_non_empty_prototype is used to limit the prototypes for which
|
||||
// we have to keep track of non-enumerable keys that can shadow keys
|
||||
// repeated on the prototype chain.
|
||||
@ -124,8 +117,6 @@ class KeyAccumulator final {
|
||||
// keys a Handle<FixedArray>. The OrderedHashSet is in-place converted to the
|
||||
// result list, a FixedArray containing all collected keys.
|
||||
Handle<FixedArray> keys_;
|
||||
Handle<Map> first_prototype_map_;
|
||||
Handle<JSReceiver> receiver_;
|
||||
Handle<JSReceiver> last_non_empty_prototype_;
|
||||
Handle<ObjectHashSet> shadowing_keys_;
|
||||
KeyCollectionMode mode_;
|
||||
@ -136,7 +127,6 @@ class KeyAccumulator final {
|
||||
// the shadow check.
|
||||
bool skip_shadow_check_ = true;
|
||||
bool may_have_elements_ = true;
|
||||
bool try_prototype_info_cache_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(KeyAccumulator);
|
||||
};
|
||||
@ -170,18 +160,13 @@ class FastKeyAccumulator {
|
||||
void Prepare();
|
||||
MaybeHandle<FixedArray> GetKeysFast(GetKeysConversion convert);
|
||||
MaybeHandle<FixedArray> GetKeysSlow(GetKeysConversion convert);
|
||||
MaybeHandle<FixedArray> GetKeysWithPrototypeInfoCache(
|
||||
GetKeysConversion convert);
|
||||
|
||||
MaybeHandle<FixedArray> GetOwnKeysWithUninitializedEnumCache();
|
||||
|
||||
bool MayHaveElements(JSReceiver receiver);
|
||||
bool TryPrototypeInfoCache(Handle<JSReceiver> receiver);
|
||||
|
||||
Isolate* isolate_;
|
||||
Handle<JSReceiver> receiver_;
|
||||
Handle<Map> first_prototype_map_;
|
||||
Handle<JSReceiver> first_prototype_;
|
||||
Handle<JSReceiver> last_non_empty_prototype_;
|
||||
KeyCollectionMode mode_;
|
||||
PropertyFilter filter_;
|
||||
@ -190,8 +175,6 @@ class FastKeyAccumulator {
|
||||
bool is_receiver_simple_enum_ = false;
|
||||
bool has_empty_prototype_ = false;
|
||||
bool may_have_elements_ = true;
|
||||
bool has_prototype_info_cache_ = false;
|
||||
bool try_prototype_info_cache_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FastKeyAccumulator);
|
||||
};
|
||||
|
@ -41,8 +41,6 @@ bool PrototypeInfo::HasObjectCreateMap() {
|
||||
|
||||
ACCESSORS(PrototypeInfo, module_namespace, Object, kJsModuleNamespaceOffset)
|
||||
ACCESSORS(PrototypeInfo, prototype_users, Object, kPrototypeUsersOffset)
|
||||
ACCESSORS(PrototypeInfo, prototype_chain_enum_cache, Object,
|
||||
kPrototypeChainEnumCacheOffset)
|
||||
WEAK_ACCESSORS(PrototypeInfo, object_create_map, kObjectCreateMapOffset)
|
||||
SMI_ACCESSORS(PrototypeInfo, registry_slot, kRegistrySlotOffset)
|
||||
SMI_ACCESSORS(PrototypeInfo, bit_field, kBitFieldOffset)
|
||||
|
@ -30,8 +30,6 @@ class PrototypeInfo : public Struct {
|
||||
// this prototype, or Smi(0) if uninitialized.
|
||||
DECL_ACCESSORS(prototype_users, Object)
|
||||
|
||||
DECL_ACCESSORS(prototype_chain_enum_cache, Object)
|
||||
|
||||
// [object_create_map]: A field caching the map for Object.create(prototype).
|
||||
static inline void SetObjectCreateMap(Handle<PrototypeInfo> info,
|
||||
Handle<Map> map);
|
||||
|
@ -5,7 +5,6 @@
|
||||
extern class PrototypeInfo extends Struct {
|
||||
js_module_namespace: JSModuleNamespace|Undefined;
|
||||
prototype_users: WeakArrayList|Zero;
|
||||
prototype_chain_enum_cache: FixedArray|Object|Undefined;
|
||||
registry_slot: Smi;
|
||||
validity_cell: Object;
|
||||
object_create_map: Weak<Map>|Undefined;
|
||||
|
@ -141,149 +141,3 @@ for_in_string_prototype();
|
||||
assertEquals(['prop2', 'prop1'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_prototype_itself_change() {
|
||||
let prototype1 = {prop: 0, prop1: 1};
|
||||
let derived1 = {prop2: 2, prop3: 3};
|
||||
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
prototype1.prop3 = 3;
|
||||
let derived2 = {prop4: 4, prop5: 5};
|
||||
Object.setPrototypeOf(derived2, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop4', 'prop5', 'prop', 'prop1', 'prop3'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_prototype_change_property() {
|
||||
let prototype1 = {prop: 0, prop1: 1};
|
||||
let derived1 = {prop2: 2, prop3: 3};
|
||||
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
prototype1.__proto__ = {prop4: 4, prop5: 5};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop', 'prop1', 'prop4', 'prop5'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
derived1.__proto__ = {prop6: 6, prop7: 7};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop6', 'prop7'], Accumulate(derived1));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_prototype_change_element() {
|
||||
let prototype1 = {prop: 0, prop1: 1};
|
||||
let derived1 = {prop2: 2, prop3: 3};
|
||||
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
prototype1[0] = 4;
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', '0', 'prop', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
derived1.__proto__ = {1: 1, 3: 3};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', '1', '3'], Accumulate(derived1));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_non_enumerable1() {
|
||||
let prototype1 = {prop: 0};
|
||||
let derived1 = Object.create(prototype1, {
|
||||
prop1: {enumerable: false, configurable: true, value: 1},
|
||||
});
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
let derived2 = {prop2: 2};
|
||||
Object.setPrototypeOf(derived2, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_non_enumerable2() {
|
||||
let prototype1 = {prop: 0};
|
||||
let derived1 = {prop1: 1};
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop1', 'prop'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
let derived2 = Object.create(prototype1, {
|
||||
prop: {enumerable: false, configurable: true, value: 0},
|
||||
prop2: {enumerable: true, configurable: true, value: 2}
|
||||
});
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_same_key1() {
|
||||
let prototype1 = {prop: 0, prop1: 1};
|
||||
let derived1 = {prop: 0, prop2: 1};
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop', 'prop2', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
let derived2 = {prop3: 3, prop4: 4};
|
||||
Object.setPrototypeOf(derived2, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop3', 'prop4', 'prop', 'prop1'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_same_key2() {
|
||||
let prototype1 = {prop: 0, prop1: 1};
|
||||
let derived1 = {prop2: 2, prop3: 3};
|
||||
Object.setPrototypeOf(derived1, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop2', 'prop3', 'prop', 'prop1'], Accumulate(derived1));
|
||||
}
|
||||
|
||||
let derived2 = {prop: 0, prop4: 4};
|
||||
Object.setPrototypeOf(derived2, prototype1);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop', 'prop4', 'prop1'], Accumulate(derived2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_redefine_property() {
|
||||
Object.prototype.prop = 0;
|
||||
let object1 = {prop1: 1, prop2: 2};
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop1', 'prop2', 'prop'], Accumulate(object1));
|
||||
}
|
||||
|
||||
let object2 = {prop3: 3, prop4: 4};
|
||||
Object.defineProperty(object2,
|
||||
'prop', {enumerable: false, configurable: true, value: 0});
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals(['prop3', 'prop4'], Accumulate(object2));
|
||||
}
|
||||
})();
|
||||
|
||||
(function for_in_empty_property() {
|
||||
let prototype1 = {prop: 0};
|
||||
let derived1 = Object.create(prototype1, {
|
||||
prop: {enumerable: false, configurable: true, value: 0}
|
||||
});
|
||||
for (let i = 0; i < 3; i++) {
|
||||
assertEquals([], Accumulate(derived1));
|
||||
}
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user