[cleanup] Unify enum cache handling.

Introduce a proper empty_descriptor_array, which has the proper layout
(length is 2 and the two fields are set properly). Also add a special
EnumCache class and a matching empty_enum_cache. The contract now is
that we only need to check the EnumLength on the map to know whether we
are allowed to use the enum cache. This greatly simplifies the handling
of the enum cache (and also the descriptor arrays), especially for the
future work on optimizing keyed access via the enum cache indices.

Bug: v8:6702
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I5ef517a3041163cd65ef003f691139ea52233e83
Reviewed-on: https://chromium-review.googlesource.com/641030
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47697}
This commit is contained in:
Benedikt Meurer 2017-08-30 06:21:29 +02:00 committed by Commit Bot
parent e84eceabbe
commit 562663d545
23 changed files with 278 additions and 294 deletions

View File

@ -4683,7 +4683,7 @@ MaybeLocal<Array> v8::Object::GetPropertyNames(Local<Context> context,
value = accumulator.GetKeys(i::GetKeysConversion::kKeepNumbers);
DCHECK(self->map()->EnumLength() == i::kInvalidEnumCacheSentinel ||
self->map()->EnumLength() == 0 ||
self->map()->instance_descriptors()->GetEnumCache() != *value);
self->map()->instance_descriptors()->GetEnumCache()->keys() != *value);
auto result = isolate->factory()->NewJSArrayWithElements(value);
RETURN_ESCAPED(Utils::ToLocal(result));
}

View File

@ -48,14 +48,12 @@ std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare(
BIND(&use_cache);
Node* map = LoadMap(object);
Node* enum_length = EnumLength(map);
GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate);
Node* descriptors = LoadMapDescriptors(map);
Node* cache_offset =
LoadObjectField(descriptors, DescriptorArray::kEnumCacheBridgeOffset);
Node* enum_cache = LoadObjectField(
cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset);
Node* enum_cache =
LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset);
Node* enum_keys = LoadObjectField(enum_cache, EnumCache::kKeysOffset);
return std::make_tuple(map, enum_cache, enum_length);
return std::make_tuple(map, enum_keys, enum_length);
}
Node* ForInBuiltinsAssembler::EnumLength(Node* map) {

View File

@ -134,12 +134,12 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
{
// The {object} has a usable enum cache, use that.
Node* object_descriptors = LoadMapDescriptors(object_map);
Node* object_enum_cache_bridge = LoadObjectField(
object_descriptors, DescriptorArray::kEnumCacheBridgeOffset);
Node* object_enum_cache = LoadObjectField(
object_enum_cache_bridge, DescriptorArray::kEnumCacheBridgeCacheOffset);
Node* object_enum_cache =
LoadObjectField(object_descriptors, DescriptorArray::kEnumCacheOffset);
Node* object_enum_keys =
LoadObjectField(object_enum_cache, EnumCache::kKeysOffset);
// Allocate a JSArray and copy the elements from the {object_enum_cache}.
// Allocate a JSArray and copy the elements from the {object_enum_keys}.
Node* array = nullptr;
Node* elements = nullptr;
Node* native_context = LoadNativeContext(context);
@ -148,7 +148,7 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
PACKED_ELEMENTS, array_map, array_length, nullptr, object_enum_length,
INTPTR_PARAMETERS);
CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_cache, elements,
CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_keys, elements,
object_enum_length, SKIP_WRITE_BARRIER);
Return(array);
}

View File

@ -509,20 +509,9 @@ FieldAccess AccessBuilder::ForFixedTypedArrayBaseExternalPointer() {
}
// static
FieldAccess AccessBuilder::ForDescriptorArrayEnumCacheBridge() {
FieldAccess AccessBuilder::ForDescriptorArrayEnumCache() {
FieldAccess access = {
kTaggedBase, DescriptorArray::kEnumCacheBridgeOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::OtherInternal(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForDescriptorArrayEnumCacheBridgeCache() {
FieldAccess access = {
kTaggedBase, DescriptorArray::kEnumCacheBridgeCacheOffset,
kTaggedBase, DescriptorArray::kEnumCacheOffset,
Handle<Name>(), MaybeHandle<Map>(),
Type::OtherInternal(), MachineType::TaggedPointer(),
kPointerWriteBarrier};
@ -940,7 +929,7 @@ ElementAccess AccessBuilder::ForFixedDoubleArrayElement() {
}
// static
ElementAccess AccessBuilder::ForDescriptorArrayEnumCacheBridgeCacheElement() {
ElementAccess AccessBuilder::ForEnumCacheKeysElement() {
ElementAccess access = {kTaggedBase, FixedArray::kHeaderSize,
Type::InternalizedString(),
MachineType::TaggedPointer(), kPointerWriteBarrier};

View File

@ -169,11 +169,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to FixedTypedArrayBase::external_pointer() field.
static FieldAccess ForFixedTypedArrayBaseExternalPointer();
// Provides access to DescriptorArray::enum_cache_bridge() field.
static FieldAccess ForDescriptorArrayEnumCacheBridge();
// Provides access to DescriptorArray::enum_cache_bridge_cache() field.
static FieldAccess ForDescriptorArrayEnumCacheBridgeCache();
// Provides access to DescriptorArray::enum_cache() field.
static FieldAccess ForDescriptorArrayEnumCache();
// Provides access to Map::bit_field() byte.
static FieldAccess ForMapBitField();
@ -285,8 +282,8 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to FixedDoubleArray elements.
static ElementAccess ForFixedDoubleArrayElement();
// Provides access to EnumCache elements.
static ElementAccess ForDescriptorArrayEnumCacheBridgeCacheElement();
// Provides access to EnumCache::keys elements.
static ElementAccess ForEnumCacheKeysElement();
// Provides access to Fixed{type}TypedArray and External{type}Array elements.
static ElementAccess ForTypedArrayElement(ExternalArrayType type,

View File

@ -270,7 +270,7 @@ Reduction JSTypeHintLowering::ReduceForInNextOperation(
receiver, cache_type, effect, control);
Node* node = jsgraph()->graph()->NewNode(
jsgraph()->simplified()->LoadElement(
AccessBuilder::ForDescriptorArrayEnumCacheBridgeCacheElement()),
AccessBuilder::ForEnumCacheKeysElement()),
cache_array, index, effect, control);
return Reduction(node);
}

View File

@ -102,6 +102,11 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
return result;
}
Handle<EnumCache> Factory::NewEnumCache(Handle<FixedArray> keys,
Handle<FixedArray> indices) {
return Handle<EnumCache>::cast(NewTuple2(keys, indices));
}
Handle<Tuple2> Factory::NewTuple2(Handle<Object> value1,
Handle<Object> value2) {
Handle<Tuple2> result = Handle<Tuple2>::cast(NewStruct(TUPLE2_TYPE));

View File

@ -126,6 +126,10 @@ class V8_EXPORT_PRIVATE Factory final {
// Create a new PrototypeInfo struct.
Handle<PrototypeInfo> NewPrototypeInfo();
// Create a new EnumCache struct.
Handle<EnumCache> NewEnumCache(Handle<FixedArray> keys,
Handle<FixedArray> indices);
// Create a new Tuple2 struct.
Handle<Tuple2> NewTuple2(Handle<Object> value1, Handle<Object> value2);

View File

@ -2426,6 +2426,11 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel, fixed_array);
fixed_array_map()->set_elements_kind(HOLEY_ELEMENTS);
ALLOCATE_PARTIAL_MAP(FIXED_ARRAY_TYPE, kVariableSizeSentinel,
fixed_cow_array)
fixed_cow_array_map()->set_elements_kind(HOLEY_ELEMENTS);
DCHECK_NE(fixed_array_map(), fixed_cow_array_map());
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, undefined);
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, null);
ALLOCATE_PARTIAL_MAP(ODDBALL_TYPE, Oddball::kSize, the_hole);
@ -2464,21 +2469,48 @@ bool Heap::CreateInitialMaps() {
// Set preliminary exception sentinel value before actually initializing it.
set_exception(null_value());
// Setup the struct maps first (needed for the EnumCache).
for (unsigned i = 0; i < arraysize(struct_table); i++) {
const StructTable& entry = struct_table[i];
Map* map;
if (!AllocatePartialMap(entry.type, entry.size).To(&map)) return false;
roots_[entry.index] = map;
}
// Allocate the empty enum cache.
{
AllocationResult allocation = Allocate(tuple2_map(), OLD_SPACE);
if (!allocation.To(&obj)) return false;
}
set_empty_enum_cache(EnumCache::cast(obj));
EnumCache::cast(obj)->set_keys(empty_fixed_array());
EnumCache::cast(obj)->set_indices(empty_fixed_array());
// Allocate the empty descriptor array.
{
AllocationResult allocation = AllocateEmptyFixedArray();
AllocationResult allocation =
AllocateUninitializedFixedArray(DescriptorArray::kFirstIndex, TENURED);
if (!allocation.To(&obj)) return false;
}
set_empty_descriptor_array(DescriptorArray::cast(obj));
DescriptorArray::cast(obj)->set(DescriptorArray::kDescriptorLengthIndex,
Smi::kZero);
DescriptorArray::cast(obj)->set(DescriptorArray::kEnumCacheIndex,
empty_enum_cache());
// Fix the instance_descriptors for the existing maps.
FinalizePartialMap(this, meta_map());
FinalizePartialMap(this, fixed_array_map());
FinalizePartialMap(this, fixed_cow_array_map());
FinalizePartialMap(this, undefined_map());
undefined_map()->set_is_undetectable();
FinalizePartialMap(this, null_map());
null_map()->set_is_undetectable();
FinalizePartialMap(this, the_hole_map());
for (unsigned i = 0; i < arraysize(struct_table); ++i) {
const StructTable& entry = struct_table[i];
FinalizePartialMap(this, Map::cast(roots_[entry.index]));
}
{ // Map allocation
#define ALLOCATE_MAP(instance_type, size, field_name) \
@ -2499,10 +2531,6 @@ bool Heap::CreateInitialMaps() {
(constructor_function_index)); \
}
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, fixed_cow_array)
fixed_cow_array_map()->set_elements_kind(HOLEY_ELEMENTS);
DCHECK_NE(fixed_array_map(), fixed_cow_array_map());
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_info)
ALLOCATE_VARSIZE_MAP(FEEDBACK_VECTOR_TYPE, feedback_vector)
@ -2580,13 +2608,6 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_VARSIZE_MAP(TRANSITION_ARRAY_TYPE, transition_array)
for (unsigned i = 0; i < arraysize(struct_table); i++) {
const StructTable& entry = struct_table[i];
Map* map;
if (!AllocateMap(entry.type, entry.size).To(&map)) return false;
roots_[entry.index] = map;
}
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, ordered_hash_table)
ALLOCATE_VARSIZE_MAP(HASH_TABLE_TYPE, unseeded_number_dictionary)
@ -4275,12 +4296,13 @@ AllocationResult Heap::AllocatePropertyArray(int length,
return result;
}
AllocationResult Heap::AllocateUninitializedFixedArray(int length) {
AllocationResult Heap::AllocateUninitializedFixedArray(
int length, PretenureFlag pretenure) {
if (length == 0) return empty_fixed_array();
HeapObject* obj = nullptr;
{
AllocationResult allocation = AllocateRawFixedArray(length, NOT_TENURED);
AllocationResult allocation = AllocateRawFixedArray(length, pretenure);
if (!allocation.To(&obj)) return allocation;
}

View File

@ -162,6 +162,7 @@ using v8::MemoryPressureLevel;
V(Map, optimized_out_map, OptimizedOutMap) \
V(Map, stale_register_map, StaleRegisterMap) \
/* Canonical empty values */ \
V(EnumCache, empty_enum_cache, EmptyEnumCache) \
V(PropertyArray, empty_property_array, EmptyPropertyArray) \
V(ByteArray, empty_byte_array, EmptyByteArray) \
V(FixedTypedArrayBase, empty_fixed_uint8_array, EmptyFixedUint8Array) \
@ -2082,7 +2083,8 @@ class Heap {
T t, int chars, uint32_t hash_field);
// Allocates an uninitialized fixed array. It must be filled by the caller.
MUST_USE_RESULT AllocationResult AllocateUninitializedFixedArray(int length);
MUST_USE_RESULT AllocationResult AllocateUninitializedFixedArray(
int length, PretenureFlag pretenure = NOT_TENURED);
// Make a copy of src and return it.
MUST_USE_RESULT inline AllocationResult CopyFixedArray(FixedArray* src);

View File

@ -2920,7 +2920,7 @@ void MarkCompactCollector::TrimDescriptorArray(Map* map,
to_trim * DescriptorArray::kEntrySize);
descriptors->SetNumberOfDescriptors(number_of_own_descriptors);
if (descriptors->HasEnumCache()) TrimEnumCache(map, descriptors);
TrimEnumCache(map, descriptors);
descriptors->Sort();
if (FLAG_unbox_double_fields) {
@ -2942,16 +2942,17 @@ void MarkCompactCollector::TrimEnumCache(Map* map,
live_enum = map->NumberOfEnumerableProperties();
}
if (live_enum == 0) return descriptors->ClearEnumCache();
EnumCache* enum_cache = descriptors->GetEnumCache();
FixedArray* enum_cache = descriptors->GetEnumCache();
int to_trim = enum_cache->length() - live_enum;
FixedArray* keys = enum_cache->keys();
int to_trim = keys->length() - live_enum;
if (to_trim <= 0) return;
heap_->RightTrimFixedArray(descriptors->GetEnumCache(), to_trim);
heap_->RightTrimFixedArray(keys, to_trim);
if (!descriptors->HasEnumIndicesCache()) return;
FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache();
heap_->RightTrimFixedArray(enum_indices_cache, to_trim);
FixedArray* indices = enum_cache->indices();
to_trim = indices->length() - live_enum;
if (to_trim <= 0) return;
heap_->RightTrimFixedArray(indices, to_trim);
}

View File

@ -432,15 +432,11 @@ void ObjectStatsCollector::RecordMapDetails(Map* map_obj) {
if (map_obj->owns_descriptors() && array != heap_->empty_descriptor_array() &&
SameLiveness(map_obj, array)) {
RecordFixedArrayHelper(map_obj, array, DESCRIPTOR_ARRAY_SUB_TYPE, 0);
if (array->HasEnumCache()) {
RecordFixedArrayHelper(array, array->GetEnumCache(), ENUM_CACHE_SUB_TYPE,
0);
}
if (array->HasEnumIndicesCache()) {
RecordFixedArrayHelper(array, array->GetEnumIndicesCache(),
EnumCache* enum_cache = array->GetEnumCache();
RecordFixedArrayHelper(array, enum_cache->keys(), ENUM_CACHE_SUB_TYPE, 0);
RecordFixedArrayHelper(array, enum_cache->indices(),
ENUM_INDICES_CACHE_SUB_TYPE, 0);
}
}
FixedArray* code_cache = map_obj->code_cache();
if (code_cache->length() > 0) {

View File

@ -259,9 +259,9 @@ void FastKeyAccumulator::Prepare() {
}
namespace {
static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
Handle<FixedArray> array,
int length) {
Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
Handle<FixedArray> array, int length) {
DCHECK_LE(length, array->length());
if (array->length() == length) return array;
return isolate->factory()->CopyFixedArrayUpTo(array, length);
@ -271,76 +271,77 @@ static Handle<FixedArray> ReduceFixedArrayTo(Isolate* isolate,
// have to make sure to never directly leak the enum cache.
Handle<FixedArray> GetFastEnumPropertyKeys(Isolate* isolate,
Handle<JSObject> object) {
Handle<Map> map(object->map());
bool cache_enum_length = map->OnlyHasSimpleProperties();
Handle<Map> map(object->map(), isolate);
Handle<FixedArray> keys(map->instance_descriptors()->GetEnumCache()->keys(),
isolate);
Handle<DescriptorArray> descs =
// Check if the {map} has a valid enum length, which implies that it
// must have a valid enum cache as well.
int enum_length = map->EnumLength();
if (enum_length != kInvalidEnumCacheSentinel) {
DCHECK(map->OnlyHasSimpleProperties());
DCHECK_LE(enum_length, keys->length());
DCHECK_EQ(enum_length, map->NumberOfEnumerableProperties());
isolate->counters()->enum_cache_hits()->Increment();
return ReduceFixedArrayTo(isolate, keys, enum_length);
}
// Determine the actual number of enumerable properties of the {map}.
enum_length = map->NumberOfEnumerableProperties();
// Check if there's already a shared enum cache on the {map}s
// DescriptorArray with sufficient number of entries.
if (enum_length <= keys->length()) {
if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
isolate->counters()->enum_cache_hits()->Increment();
return ReduceFixedArrayTo(isolate, keys, enum_length);
}
Handle<DescriptorArray> descriptors =
Handle<DescriptorArray>(map->instance_descriptors(), isolate);
int own_property_count = map->EnumLength();
// If the enum length of the given map is set to kInvalidEnumCache, this
// means that the map itself has never used the present enum cache. The
// first step to using the cache is to set the enum length of the map by
// counting the number of own descriptors that are ENUMERABLE_STRINGS.
if (own_property_count == kInvalidEnumCacheSentinel) {
own_property_count = map->NumberOfEnumerableProperties();
} else {
DCHECK_EQ(own_property_count, map->NumberOfEnumerableProperties());
}
if (descs->HasEnumCache()) {
Handle<FixedArray> keys(descs->GetEnumCache(), isolate);
// In case the number of properties required in the enum are actually
// present, we can reuse the enum cache. Otherwise, this means that the
// enum cache was generated for a previous (smaller) version of the
// Descriptor Array. In that case we regenerate the enum cache.
if (own_property_count <= keys->length()) {
isolate->counters()->enum_cache_hits()->Increment();
if (cache_enum_length) map->SetEnumLength(own_property_count);
return ReduceFixedArrayTo(isolate, keys, own_property_count);
}
}
if (descs->IsEmpty()) {
isolate->counters()->enum_cache_hits()->Increment();
if (cache_enum_length) map->SetEnumLength(0);
return isolate->factory()->empty_fixed_array();
}
isolate->counters()->enum_cache_misses()->Increment();
int nod = map->NumberOfOwnDescriptors();
Handle<FixedArray> storage =
isolate->factory()->NewFixedArray(own_property_count);
Handle<FixedArray> indices =
isolate->factory()->NewFixedArray(own_property_count);
int size = map->NumberOfOwnDescriptors();
// Create the keys array.
int index = 0;
for (int i = 0; i < size; i++) {
PropertyDetails details = descs->GetDetails(i);
bool fields_only = true;
keys = isolate->factory()->NewFixedArray(enum_length);
for (int i = 0; i < nod; i++) {
DisallowHeapAllocation no_gc;
PropertyDetails details = descriptors->GetDetails(i);
if (details.IsDontEnum()) continue;
Object* key = descs->GetKey(i);
Object* key = descriptors->GetKey(i);
if (key->IsSymbol()) continue;
storage->set(index, key);
if (!indices.is_null()) {
if (details.location() == kField) {
DCHECK_EQ(kData, details.kind());
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
int load_by_field_index = field_index.GetLoadByFieldIndex();
indices->set(index, Smi::FromInt(load_by_field_index));
} else {
indices = Handle<FixedArray>();
}
}
keys->set(index, key);
if (details.location() != kField) fields_only = false;
index++;
}
DCHECK(index == storage->length());
DCHECK_EQ(index, keys->length());
DescriptorArray::SetEnumCache(descs, isolate, storage, indices);
if (cache_enum_length) {
map->SetEnumLength(own_property_count);
// Optionally also create the indices array.
Handle<FixedArray> indices = isolate->factory()->empty_fixed_array();
if (fields_only) {
indices = isolate->factory()->NewFixedArray(enum_length);
index = 0;
for (int i = 0; i < nod; i++) {
DisallowHeapAllocation no_gc;
PropertyDetails details = descriptors->GetDetails(i);
if (details.IsDontEnum()) continue;
Object* key = descriptors->GetKey(i);
if (key->IsSymbol()) continue;
DCHECK_EQ(kData, details.kind());
DCHECK_EQ(kField, details.location());
FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
indices->set(index, Smi::FromInt(field_index.GetLoadByFieldIndex()));
index++;
}
return storage;
DCHECK_EQ(index, indices->length());
}
DescriptorArray::SetEnumCache(descriptors, isolate, keys, indices);
if (map->OnlyHasSimpleProperties()) map->SetEnumLength(enum_length);
return keys;
}
template <bool fast_properties>

View File

@ -370,6 +370,15 @@ void JSObject::JSObjectVerify() {
field_type));
}
}
if (map()->EnumLength() != kInvalidEnumCacheSentinel) {
EnumCache* enum_cache = descriptors->GetEnumCache();
FixedArray* keys = enum_cache->keys();
FixedArray* indices = enum_cache->indices();
DCHECK_LE(map()->EnumLength(), keys->length());
DCHECK_IMPLIES(indices != isolate->heap()->empty_fixed_array(),
keys->length() == indices->length());
}
}
// If a GC was caused while constructing this object, the elements
@ -420,7 +429,8 @@ void Map::MapVerify() {
void Map::DictionaryMapVerify() {
MapVerify();
CHECK(is_dictionary_map());
CHECK(instance_descriptors()->IsEmpty());
CHECK_EQ(kInvalidEnumCacheSentinel, EnumLength());
CHECK_EQ(GetHeap()->empty_descriptor_array(), instance_descriptors());
CHECK_EQ(0, unused_property_fields());
CHECK_EQ(Map::GetVisitorId(this), visitor_id());
}
@ -435,6 +445,13 @@ void FixedArray::FixedArrayVerify() {
Object* e = get(i);
VerifyPointer(e);
}
Heap* heap = GetHeap();
if (this == heap->empty_descriptor_array()) {
DescriptorArray* descriptors = DescriptorArray::cast(this);
CHECK_EQ(2, length());
CHECK_EQ(0, descriptors->number_of_descriptors());
CHECK_EQ(heap->empty_enum_cache(), descriptors->GetEnumCache());
}
}
void PropertyArray::PropertyArrayVerify() {
@ -1266,9 +1283,15 @@ void PrototypeInfo::PrototypeInfoVerify() {
void Tuple2::Tuple2Verify() {
CHECK(IsTuple2());
Heap* heap = GetHeap();
if (this == heap->empty_enum_cache()) {
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->keys());
CHECK_EQ(heap->empty_fixed_array(), EnumCache::cast(this)->indices());
} else {
VerifyObjectField(kValue1Offset);
VerifyObjectField(kValue2Offset);
}
}
void Tuple3::Tuple3Verify() {
CHECK(IsTuple3());

View File

@ -314,6 +314,8 @@ bool HeapObject::IsJSCollection() const { return IsJSMap() || IsJSSet(); }
bool HeapObject::IsDescriptorArray() const { return IsFixedArray(); }
bool HeapObject::IsEnumCache() const { return IsTuple2(); }
bool HeapObject::IsFrameArray() const { return IsFixedArray(); }
bool HeapObject::IsArrayList() const { return IsFixedArray(); }
@ -551,6 +553,7 @@ CAST_ACCESSOR(ContextExtension)
CAST_ACCESSOR(DeoptimizationInputData)
CAST_ACCESSOR(DependentCode)
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(EnumCache)
CAST_ACCESSOR(FixedArray)
CAST_ACCESSOR(FixedArrayBase)
CAST_ACCESSOR(FixedDoubleArray)
@ -2024,23 +2027,16 @@ Object** FixedArray::RawFieldOfElementAt(int index) {
return HeapObject::RawField(this, OffsetOfElementAt(index));
}
bool DescriptorArray::IsEmpty() {
DCHECK(length() >= kFirstIndex ||
this == GetHeap()->empty_descriptor_array());
return length() < kFirstIndex;
}
ACCESSORS(EnumCache, keys, FixedArray, kKeysOffset)
ACCESSORS(EnumCache, indices, FixedArray, kIndicesOffset)
int DescriptorArray::number_of_descriptors() {
DCHECK(length() >= kFirstIndex || IsEmpty());
int len = length();
return len == 0 ? 0 : Smi::ToInt(get(kDescriptorLengthIndex));
return Smi::ToInt(get(kDescriptorLengthIndex));
}
int DescriptorArray::number_of_descriptors_storage() {
int len = length();
return len == 0 ? 0 : (len - kFirstIndex) / kEntrySize;
return (length() - kFirstIndex) / kEntrySize;
}
@ -2050,8 +2046,7 @@ int DescriptorArray::NumberOfSlackDescriptors() {
void DescriptorArray::SetNumberOfDescriptors(int number_of_descriptors) {
WRITE_FIELD(
this, kDescriptorLengthOffset, Smi::FromInt(number_of_descriptors));
set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
}
@ -2059,40 +2054,14 @@ inline int DescriptorArray::number_of_entries() {
return number_of_descriptors();
}
bool DescriptorArray::HasEnumCache() {
return !IsEmpty() && !get(kEnumCacheBridgeIndex)->IsSmi();
}
void DescriptorArray::CopyEnumCacheFrom(DescriptorArray* array) {
set(kEnumCacheBridgeIndex, array->get(kEnumCacheBridgeIndex));
set(kEnumCacheIndex, array->get(kEnumCacheIndex));
}
FixedArray* DescriptorArray::GetEnumCache() {
DCHECK(HasEnumCache());
FixedArray* bridge = FixedArray::cast(get(kEnumCacheBridgeIndex));
return FixedArray::cast(bridge->get(kEnumCacheBridgeCacheIndex));
EnumCache* DescriptorArray::GetEnumCache() {
return EnumCache::cast(get(kEnumCacheIndex));
}
bool DescriptorArray::HasEnumIndicesCache() {
if (IsEmpty()) return false;
Object* object = get(kEnumCacheBridgeIndex);
if (object->IsSmi()) return false;
FixedArray* bridge = FixedArray::cast(object);
return !bridge->get(kEnumCacheBridgeIndicesCacheIndex)->IsSmi();
}
FixedArray* DescriptorArray::GetEnumIndicesCache() {
DCHECK(HasEnumIndicesCache());
FixedArray* bridge = FixedArray::cast(get(kEnumCacheBridgeIndex));
return FixedArray::cast(bridge->get(kEnumCacheBridgeIndicesCacheIndex));
}
// Perform a binary search in a fixed array.
template <SearchMode search_mode, typename T>
int BinarySearch(T* array, Name* name, int valid_entries,
@ -2243,7 +2212,6 @@ int Map::EnumLength() const { return EnumLengthBits::decode(bit_field3()); }
void Map::SetEnumLength(int length) {
if (length != kInvalidEnumCacheSentinel) {
DCHECK(length >= 0);
DCHECK(length == 0 || instance_descriptors()->HasEnumCache());
DCHECK(length <= NumberOfOwnDescriptors());
}
set_bit_field3(EnumLengthBits::update(bit_field3(), length));

View File

@ -5045,9 +5045,7 @@ void Map::EnsureDescriptorSlack(Handle<Map> map, int slack) {
// on a cache always being available once it is set. If the map has more
// enumerated descriptors than available in the original cache, the cache
// will be lazily replaced by the extended cache when needed.
if (descriptors->HasEnumCache()) {
new_descriptors->CopyEnumCacheFrom(*descriptors);
}
Isolate* isolate = map->GetIsolate();
// Replace descriptors by new_descriptors in all maps that share it. The old
@ -10413,12 +10411,12 @@ Handle<DescriptorArray> DescriptorArray::Allocate(Isolate* isolate,
factory->NewFixedArray(LengthFor(size), pretenure);
result->set(kDescriptorLengthIndex, Smi::FromInt(number_of_descriptors));
result->set(kEnumCacheBridgeIndex, Smi::kZero);
result->set(kEnumCacheIndex, isolate->heap()->empty_enum_cache());
return Handle<DescriptorArray>::cast(result);
}
void DescriptorArray::ClearEnumCache() {
set(kEnumCacheBridgeIndex, Smi::kZero);
set(kEnumCacheIndex, GetHeap()->empty_enum_cache());
}
void DescriptorArray::Replace(int index, Descriptor* descriptor) {
@ -10426,27 +10424,17 @@ void DescriptorArray::Replace(int index, Descriptor* descriptor) {
Set(index, descriptor);
}
// static
void DescriptorArray::SetEnumCache(Handle<DescriptorArray> descriptors,
Isolate* isolate,
Handle<FixedArray> new_cache,
Handle<FixedArray> new_index_cache) {
DCHECK(!descriptors->IsEmpty());
FixedArray* bridge_storage;
bool needs_new_enum_cache = !descriptors->HasEnumCache();
if (needs_new_enum_cache) {
bridge_storage = *isolate->factory()->NewFixedArray(
DescriptorArray::kEnumCacheBridgeLength);
Isolate* isolate, Handle<FixedArray> keys,
Handle<FixedArray> indices) {
EnumCache* enum_cache = descriptors->GetEnumCache();
if (enum_cache == isolate->heap()->empty_enum_cache()) {
enum_cache = *isolate->factory()->NewEnumCache(keys, indices);
descriptors->set(kEnumCacheIndex, enum_cache);
} else {
bridge_storage = FixedArray::cast(descriptors->get(kEnumCacheBridgeIndex));
}
bridge_storage->set(kEnumCacheBridgeCacheIndex, *new_cache);
bridge_storage->set(
kEnumCacheBridgeIndicesCacheIndex,
new_index_cache.is_null() ? Object::cast(Smi::kZero) : *new_index_cache);
if (needs_new_enum_cache) {
descriptors->set(kEnumCacheBridgeIndex, bridge_storage);
enum_cache->set_keys(*keys);
enum_cache->set_indices(*indices);
}
}
@ -10595,8 +10583,6 @@ int HandlerTable::LookupReturn(int pc_offset) {
#ifdef DEBUG
bool DescriptorArray::IsEqualTo(DescriptorArray* other) {
if (IsEmpty()) return other->IsEmpty();
if (other->IsEmpty()) return false;
if (length() != other->length()) return false;
for (int i = 0; i < length(); ++i) {
if (get(i) != other->get(i)) return false;

View File

@ -146,6 +146,7 @@
// - AccessCheckInfo
// - InterceptorInfo
// - CallHandlerInfo
// - EnumCache
// - TemplateInfo
// - FunctionTemplateInfo
// - ObjectTemplateInfo
@ -920,6 +921,7 @@ class AllocationSite;
class Cell;
class ConsString;
class ElementsAccessor;
class EnumCache;
class FindAndReplacePattern;
class FixedArrayBase;
class PropertyArray;
@ -988,6 +990,7 @@ template <class C> inline bool Is(Object* obj);
V(DeoptimizationInputData) \
V(DependentCode) \
V(DescriptorArray) \
V(EnumCache) \
V(External) \
V(ExternalOneByteString) \
V(ExternalString) \

View File

@ -18,13 +18,26 @@ class Handle;
class Isolate;
// An EnumCache is a pair used to hold keys and indices caches.
class EnumCache : public Tuple2 {
public:
DECL_ACCESSORS(keys, FixedArray)
DECL_ACCESSORS(indices, FixedArray)
DECL_CAST(EnumCache)
// Layout description.
static const int kKeysOffset = kValue1Offset;
static const int kIndicesOffset = kValue2Offset;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(EnumCache);
};
// A DescriptorArray is a fixed array used to hold instance descriptors.
// The format of the these objects is:
// The format of these objects is:
// [0]: Number of descriptors
// [1]: Either Smi(0) if uninitialized,
// or enum cache bridge (FixedArray[2]):
// [0]: enum cache: FixedArray containing all own enumerable keys
// [1]: either Smi(0) or pointer to fixed array with indices
// [1]: Enum cache.
// [2]: first key (and internalized String)
// [3]: first descriptor details (see PropertyDetails)
// [4]: first value for constants | Smi(1) when not usedA
@ -32,11 +45,6 @@ class Isolate;
// [2 + number of descriptors * 3]: start of slack
class DescriptorArray : public FixedArray {
public:
// Returns true for both shared empty_descriptor_array and for smis, which the
// map uses to encode additional bit fields when the descriptor array is not
// yet used.
inline bool IsEmpty();
// Returns the number of descriptors in the array.
inline int number_of_descriptors();
inline int number_of_descriptors_storage();
@ -45,18 +53,14 @@ class DescriptorArray : public FixedArray {
inline void SetNumberOfDescriptors(int number_of_descriptors);
inline int number_of_entries();
inline bool HasEnumCache();
inline bool HasEnumIndicesCache();
inline FixedArray* GetEnumCache();
inline FixedArray* GetEnumIndicesCache();
inline EnumCache* GetEnumCache();
void ClearEnumCache();
inline void CopyEnumCacheFrom(DescriptorArray* array);
// Initialize or change the enum cache,
// using the supplied storage for the small "bridge".
static void SetEnumCache(Handle<DescriptorArray> descriptors,
Isolate* isolate, Handle<FixedArray> new_cache,
Handle<FixedArray> new_index_cache);
Isolate* isolate, Handle<FixedArray> keys,
Handle<FixedArray> indices);
// Accessors for fetching instance descriptor at descriptor number.
inline Name* GetKey(int descriptor_number);
@ -122,22 +126,13 @@ class DescriptorArray : public FixedArray {
static const int kNotFound = -1;
static const int kDescriptorLengthIndex = 0;
static const int kEnumCacheBridgeIndex = 1;
static const int kEnumCacheIndex = 1;
static const int kFirstIndex = 2;
// The length of the "bridge" to the enum cache.
static const int kEnumCacheBridgeLength = 2;
static const int kEnumCacheBridgeCacheIndex = 0;
static const int kEnumCacheBridgeIndicesCacheIndex = 1;
// Layout description.
static const int kDescriptorLengthOffset = FixedArray::kHeaderSize;
static const int kEnumCacheBridgeOffset =
kDescriptorLengthOffset + kPointerSize;
static const int kFirstOffset = kEnumCacheBridgeOffset + kPointerSize;
// Layout description for the bridge array.
static const int kEnumCacheBridgeCacheOffset = FixedArray::kHeaderSize;
static const int kEnumCacheOffset = kDescriptorLengthOffset + kPointerSize;
static const int kFirstOffset = kEnumCacheOffset + kPointerSize;
// Layout of descriptor.
// Naming is consistent with Dictionary classes for easy templating.

View File

@ -126,12 +126,7 @@ RUNTIME_FUNCTION_RETURN_TRIPLE(Runtime_ForInPrepare) {
Handle<DescriptorArray> descriptors(cache_map->instance_descriptors(),
isolate);
cache_length = cache_map->EnumLength();
if (cache_length && descriptors->HasEnumCache()) {
cache_array = handle(descriptors->GetEnumCache(), isolate);
} else {
cache_array = isolate->factory()->empty_fixed_array();
cache_length = 0;
}
cache_array = handle(descriptors->GetEnumCache()->keys(), isolate);
} else {
cache_array = Handle<FixedArray>::cast(cache_type);
cache_length = cache_array->length();

View File

@ -1754,7 +1754,7 @@ Maybe<uint32_t> ValueDeserializer::ReadJSObjectProperties(
bool transitioning = true;
Handle<Map> map(object->map(), isolate_);
DCHECK(!map->is_dictionary_map());
DCHECK(map->instance_descriptors()->IsEmpty());
DCHECK_EQ(0, map->instance_descriptors()->number_of_descriptors());
std::vector<Handle<Object>> properties;
properties.reserve(8);

View File

@ -150,7 +150,7 @@ TEST(StressJS) {
// Patch the map to have an accessor for "get".
Handle<Map> map(function->initial_map());
Handle<DescriptorArray> instance_descriptors(map->instance_descriptors());
CHECK(instance_descriptors->IsEmpty());
CHECK_EQ(0, instance_descriptors->number_of_descriptors());
PropertyAttributes attrs = NONE;
Handle<AccessorInfo> foreign = TestAccessorInfo(isolate, attrs);

View File

@ -2536,10 +2536,9 @@ THREADED_TEST(AccessorIsPreservedOnAttributeChange) {
LocalContext env;
v8::Local<v8::Value> res = CompileRun("var a = []; a;");
i::Handle<i::JSReceiver> a(v8::Utils::OpenHandle(v8::Object::Cast(*res)));
CHECK(a->map()->instance_descriptors()->IsFixedArray());
CHECK_GT(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0);
CHECK_EQ(1, a->map()->instance_descriptors()->number_of_descriptors());
CompileRun("Object.defineProperty(a, 'length', { writable: false });");
CHECK_EQ(0, i::FixedArray::cast(a->map()->instance_descriptors())->length());
CHECK_EQ(0, a->map()->instance_descriptors()->number_of_descriptors());
// But we should still have an AccessorInfo.
i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length")));
i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR);

View File

@ -247,22 +247,22 @@ KNOWN_MAPS = {
0x03ee1: (171, "UnseededNumberDictionaryMap"),
0x03f39: (190, "ExternalMap"),
0x03f91: (106, "NativeSourceStringMap"),
0x03fe9: (152, "InterceptorInfoMap"),
0x04041: (208, "JSPromiseCapabilityMap"),
0x04099: (149, "AccessorInfoMap"),
0x040f1: (150, "AccessorPairMap"),
0x04149: (151, "AccessCheckInfoMap"),
0x041a1: (153, "FunctionTemplateInfoMap"),
0x041f9: (154, "ObjectTemplateInfoMap"),
0x04251: (155, "AllocationSiteMap"),
0x042a9: (156, "AllocationMementoMap"),
0x04301: (158, "AliasedArgumentsEntryMap"),
0x04359: (159, "PromiseResolveThenableJobInfoMap"),
0x043b1: (160, "PromiseReactionJobInfoMap"),
0x04409: (161, "DebugInfoMap"),
0x04461: (162, "StackFrameInfoMap"),
0x044b9: (163, "PrototypeInfoMap"),
0x04511: (164, "Tuple2Map"),
0x03fe9: (164, "Tuple2Map"),
0x04041: (152, "InterceptorInfoMap"),
0x04099: (208, "JSPromiseCapabilityMap"),
0x040f1: (149, "AccessorInfoMap"),
0x04149: (150, "AccessorPairMap"),
0x041a1: (151, "AccessCheckInfoMap"),
0x041f9: (153, "FunctionTemplateInfoMap"),
0x04251: (154, "ObjectTemplateInfoMap"),
0x042a9: (155, "AllocationSiteMap"),
0x04301: (156, "AllocationMementoMap"),
0x04359: (158, "AliasedArgumentsEntryMap"),
0x043b1: (159, "PromiseResolveThenableJobInfoMap"),
0x04409: (160, "PromiseReactionJobInfoMap"),
0x04461: (161, "DebugInfoMap"),
0x044b9: (162, "StackFrameInfoMap"),
0x04511: (163, "PrototypeInfoMap"),
0x04569: (165, "Tuple3Map"),
0x045c1: (166, "ContextExtensionMap"),
0x04619: (167, "ModuleMap"),
@ -274,47 +274,47 @@ KNOWN_MAPS = {
KNOWN_OBJECTS = {
("OLD_SPACE", 0x02201): "NullValue",
("OLD_SPACE", 0x02231): "EmptyDescriptorArray",
("OLD_SPACE", 0x02241): "EmptyFixedArray",
("OLD_SPACE", 0x02251): "UninitializedValue",
("OLD_SPACE", 0x022d1): "UndefinedValue",
("OLD_SPACE", 0x02301): "NanValue",
("OLD_SPACE", 0x02311): "TheHoleValue",
("OLD_SPACE", 0x02361): "HoleNanValue",
("OLD_SPACE", 0x02371): "TrueValue",
("OLD_SPACE", 0x023e1): "FalseValue",
("OLD_SPACE", 0x02431): "empty_string",
("OLD_SPACE", 0x02449): "EmptyScopeInfo",
("OLD_SPACE", 0x02459): "ArgumentsMarker",
("OLD_SPACE", 0x024b1): "Exception",
("OLD_SPACE", 0x02509): "TerminationException",
("OLD_SPACE", 0x02569): "OptimizedOut",
("OLD_SPACE", 0x025c1): "StaleRegister",
("OLD_SPACE", 0x02619): "EmptyByteArray",
("OLD_SPACE", 0x02629): "EmptyFixedUint8Array",
("OLD_SPACE", 0x02649): "EmptyFixedInt8Array",
("OLD_SPACE", 0x02669): "EmptyFixedUint16Array",
("OLD_SPACE", 0x02689): "EmptyFixedInt16Array",
("OLD_SPACE", 0x026a9): "EmptyFixedUint32Array",
("OLD_SPACE", 0x026c9): "EmptyFixedInt32Array",
("OLD_SPACE", 0x026e9): "EmptyFixedFloat32Array",
("OLD_SPACE", 0x02709): "EmptyFixedFloat64Array",
("OLD_SPACE", 0x02729): "EmptyFixedUint8ClampedArray",
("OLD_SPACE", 0x02749): "EmptyScript",
("OLD_SPACE", 0x027c9): "UndefinedCell",
("OLD_SPACE", 0x027d9): "EmptySloppyArgumentsElements",
("OLD_SPACE", 0x027f9): "EmptySlowElementDictionary",
("OLD_SPACE", 0x02841): "EmptyPropertyCell",
("OLD_SPACE", 0x02869): "EmptyWeakCell",
("OLD_SPACE", 0x02879): "ArrayProtector",
("OLD_SPACE", 0x028a1): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x028b1): "SpeciesProtector",
("OLD_SPACE", 0x028d9): "StringLengthProtector",
("OLD_SPACE", 0x028e9): "FastArrayIterationProtector",
("OLD_SPACE", 0x028f9): "ArrayIteratorProtector",
("OLD_SPACE", 0x02921): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02949): "InfinityValue",
("OLD_SPACE", 0x02959): "MinusZeroValue",
("OLD_SPACE", 0x02969): "MinusInfinityValue",
("OLD_SPACE", 0x02251): "EmptyFixedArray",
("OLD_SPACE", 0x02261): "UninitializedValue",
("OLD_SPACE", 0x022e1): "UndefinedValue",
("OLD_SPACE", 0x02311): "NanValue",
("OLD_SPACE", 0x02321): "TheHoleValue",
("OLD_SPACE", 0x02371): "HoleNanValue",
("OLD_SPACE", 0x02381): "TrueValue",
("OLD_SPACE", 0x023f1): "FalseValue",
("OLD_SPACE", 0x02441): "empty_string",
("OLD_SPACE", 0x02459): "EmptyScopeInfo",
("OLD_SPACE", 0x02469): "ArgumentsMarker",
("OLD_SPACE", 0x024c1): "Exception",
("OLD_SPACE", 0x02519): "TerminationException",
("OLD_SPACE", 0x02579): "OptimizedOut",
("OLD_SPACE", 0x025d1): "StaleRegister",
("OLD_SPACE", 0x02629): "EmptyByteArray",
("OLD_SPACE", 0x02639): "EmptyFixedUint8Array",
("OLD_SPACE", 0x02659): "EmptyFixedInt8Array",
("OLD_SPACE", 0x02679): "EmptyFixedUint16Array",
("OLD_SPACE", 0x02699): "EmptyFixedInt16Array",
("OLD_SPACE", 0x026b9): "EmptyFixedUint32Array",
("OLD_SPACE", 0x026d9): "EmptyFixedInt32Array",
("OLD_SPACE", 0x026f9): "EmptyFixedFloat32Array",
("OLD_SPACE", 0x02719): "EmptyFixedFloat64Array",
("OLD_SPACE", 0x02739): "EmptyFixedUint8ClampedArray",
("OLD_SPACE", 0x02759): "EmptyScript",
("OLD_SPACE", 0x027d9): "UndefinedCell",
("OLD_SPACE", 0x027e9): "EmptySloppyArgumentsElements",
("OLD_SPACE", 0x02809): "EmptySlowElementDictionary",
("OLD_SPACE", 0x02851): "EmptyPropertyCell",
("OLD_SPACE", 0x02879): "EmptyWeakCell",
("OLD_SPACE", 0x02889): "ArrayProtector",
("OLD_SPACE", 0x028b1): "IsConcatSpreadableProtector",
("OLD_SPACE", 0x028c1): "SpeciesProtector",
("OLD_SPACE", 0x028e9): "StringLengthProtector",
("OLD_SPACE", 0x028f9): "FastArrayIterationProtector",
("OLD_SPACE", 0x02909): "ArrayIteratorProtector",
("OLD_SPACE", 0x02931): "ArrayBufferNeuteringProtector",
("OLD_SPACE", 0x02959): "InfinityValue",
("OLD_SPACE", 0x02969): "MinusZeroValue",
("OLD_SPACE", 0x02979): "MinusInfinityValue",
}
# List of known V8 Frame Markers.