Commit 68c944c2 authored by verwaest@chromium.org's avatar verwaest@chromium.org

In-place shrinking of descriptor arrays with non-live transitions.

Instead of overwriting non-live transitions with NULL_DESCRIPTORs, we remove them from the array by compacting the array (shifting live values to the left) and in-place trimming the array. If the final descriptor array contains no live values (only contained transitions which are now all cleared), we move bit_field3 back from the descriptor array to the map. The descriptor array itself will be collected in the next GC.

BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com/10575032

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11922 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 390580e4
......@@ -520,9 +520,11 @@ bool Call::ComputeTarget(Handle<Map> type, Handle<String> name) {
return false;
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
// Perhaps something interesting is up in the prototype chain...
break;
case NONEXISTENT:
UNREACHABLE();
break;
}
}
// If we reach the end of the prototype chain, we don't know the target.
......
......@@ -2193,13 +2193,13 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
// Ignore non-properties.
break;
case NORMAL:
// Do not occur since the from object has fast properties.
case HANDLER:
case INTERCEPTOR:
case NONEXISTENT:
// No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();
break;
......
......@@ -922,25 +922,18 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
Handle<Object> descriptors) {
v8::NeanderArray callbacks(descriptors);
int nof_callbacks = callbacks.length();
int descriptor_count = array->number_of_descriptors();
Handle<DescriptorArray> result =
NewDescriptorArray(array->number_of_descriptors() + nof_callbacks);
// Number of descriptors added to the result so far.
int descriptor_count = 0;
NewDescriptorArray(descriptor_count + nof_callbacks);
// Ensure that marking will not progress and change color of objects.
DescriptorArray::WhitenessWitness witness(*result);
// Copy the descriptors from the array.
for (int i = 0; i < array->number_of_descriptors(); i++) {
if (!array->IsNullDescriptor(i)) {
DescriptorArray::CopyFrom(result, descriptor_count++, array, i, witness);
}
for (int i = 0; i < descriptor_count; i++) {
DescriptorArray::CopyFrom(result, i, array, i, witness);
}
// Number of duplicates detected.
int duplicates = 0;
// Fill in new callback descriptors. Process the callbacks from
// back to front so that the last callback with a given name takes
// precedence over previously added callbacks with that name.
......@@ -956,18 +949,14 @@ Handle<DescriptorArray> Factory::CopyAppendCallbackDescriptors(
CallbacksDescriptor desc(*key, *entry, entry->property_attributes());
result->Set(descriptor_count, &desc, witness);
descriptor_count++;
} else {
duplicates++;
}
}
// If duplicates were detected, allocate a result of the right size
// and transfer the elements.
if (duplicates > 0) {
int number_of_descriptors = result->number_of_descriptors() - duplicates;
Handle<DescriptorArray> new_result =
NewDescriptorArray(number_of_descriptors);
for (int i = 0; i < number_of_descriptors; i++) {
if (descriptor_count < result->length()) {
Handle<DescriptorArray> new_result = NewDescriptorArray(descriptor_count);
for (int i = 0; i < descriptor_count; i++) {
DescriptorArray::CopyFrom(new_result, i, result, i, witness);
}
result = new_result;
......
......@@ -1298,7 +1298,7 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup,
static bool StoreICableLookup(LookupResult* lookup) {
// Bail out if we didn't find a result.
if (!lookup->IsFound() || lookup->type() == NULL_DESCRIPTOR) return false;
if (!lookup->IsFound()) return false;
// Bail out if inline caching is not allowed.
if (!lookup->IsCacheable()) return false;
......@@ -1434,10 +1434,10 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
Handle<Object> value) {
ASSERT(!receiver->IsJSGlobalProxy());
ASSERT(StoreICableLookup(lookup));
ASSERT(lookup->IsFound());
// These are not cacheable, so we never see such LookupResults here.
ASSERT(!lookup->IsHandler());
// We get only called for properties or transitions, see StoreICableLookup.
ASSERT(lookup->type() != NULL_DESCRIPTOR);
// If the property has a non-field type allowing map transitions
// where there is extra room in the object, we leave the IC in its
......@@ -1509,8 +1509,8 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
case CONSTANT_FUNCTION:
case CONSTANT_TRANSITION:
return;
case NONEXISTENT:
case HANDLER:
case NULL_DESCRIPTOR:
UNREACHABLE();
return;
}
......@@ -1936,10 +1936,10 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
Handle<Object> value) {
ASSERT(!receiver->IsJSGlobalProxy());
ASSERT(StoreICableLookup(lookup));
ASSERT(lookup->IsFound());
// These are not cacheable, so we never see such LookupResults here.
ASSERT(!lookup->IsHandler());
// We get only called for properties or transitions, see StoreICableLookup.
ASSERT(lookup->type() != NULL_DESCRIPTOR);
// If the property has a non-field type allowing map transitions
// where there is extra room in the object, we leave the IC in its
......@@ -1978,7 +1978,7 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
: generic_stub();
break;
case HANDLER:
case NULL_DESCRIPTOR:
case NONEXISTENT:
UNREACHABLE();
return;
}
......
......@@ -1901,8 +1901,7 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
}
// If the descriptor contains a transition (value is a Map), we don't mark the
// value as live. It might be set to the NULL_DESCRIPTOR in
// ClearNonLiveTransitions later.
// value as live. It might be removed by ClearNonLiveTransitions later.
for (int i = 0; i < descriptors->number_of_descriptors(); ++i) {
Object** key_slot = descriptors->GetKeySlot(i);
Object* key = *key_slot;
......@@ -1940,7 +1939,9 @@ void Marker<T>::MarkDescriptorArray(DescriptorArray* descriptors) {
break;
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
break;
case NONEXISTENT:
UNREACHABLE();
break;
}
}
......
......@@ -953,7 +953,9 @@ bool DescriptorArray::IsConsistentWithBackPointers(Map* current_map) {
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
case NULL_DESCRIPTOR:
break;
case NONEXISTENT:
UNREACHABLE();
break;
}
}
......
......@@ -1863,7 +1863,7 @@ void FixedArray::set_unchecked(Heap* heap,
void FixedArray::set_null_unchecked(Heap* heap, int index) {
ASSERT(index >= 0 && index < this->length());
ASSERT(!HEAP->InNewSpace(heap->null_value()));
ASSERT(!heap->InNewSpace(heap->null_value()));
WRITE_FIELD(this, kHeaderSize + index * kPointerSize, heap->null_value());
}
......@@ -1976,6 +1976,17 @@ String* DescriptorArray::GetKey(int descriptor_number) {
}
void DescriptorArray::SetKeyUnchecked(Heap* heap,
int descriptor_number,
String* key) {
ASSERT(descriptor_number < number_of_descriptors());
set_unchecked(heap,
ToKeyIndex(descriptor_number),
key,
UPDATE_WRITE_BARRIER);
}
Object** DescriptorArray::GetValueSlot(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
return HeapObject::RawField(
......@@ -1990,12 +2001,24 @@ Object* DescriptorArray::GetValue(int descriptor_number) {
}
void DescriptorArray::SetNullValueUnchecked(int descriptor_number, Heap* heap) {
void DescriptorArray::SetNullValueUnchecked(Heap* heap, int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
set_null_unchecked(heap, ToValueIndex(descriptor_number));
}
void DescriptorArray::SetValueUnchecked(Heap* heap,
int descriptor_number,
Object* value) {
ASSERT(descriptor_number < number_of_descriptors());
set_unchecked(heap,
ToValueIndex(descriptor_number),
value,
UPDATE_WRITE_BARRIER);
}
PropertyDetails DescriptorArray::GetDetails(int descriptor_number) {
ASSERT(descriptor_number < number_of_descriptors());
Object* details = get(ToDetailsIndex(descriptor_number));
......@@ -2059,19 +2082,16 @@ bool DescriptorArray::IsTransitionOnly(int descriptor_number) {
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
case NULL_DESCRIPTOR:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;
}
bool DescriptorArray::IsNullDescriptor(int descriptor_number) {
return GetType(descriptor_number) == NULL_DESCRIPTOR;
}
void DescriptorArray::Get(int descriptor_number, Descriptor* desc) {
desc->Init(GetKey(descriptor_number),
GetValue(descriptor_number),
......@@ -3464,10 +3484,23 @@ int Map::bit_field3() {
}
void Map::ClearDescriptorArray() {
int bitfield3 = bit_field3();
#ifdef DEBUG
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (!object->IsSmi()) {
ZapInstanceDescriptors();
}
#endif
WRITE_FIELD(this,
kInstanceDescriptorsOrBitField3Offset,
Smi::FromInt(bitfield3));
}
void Map::set_bit_field3(int value) {
ASSERT(Smi::IsValid(value));
Object* object = READ_FIELD(this,
kInstanceDescriptorsOrBitField3Offset);
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (object->IsSmi()) {
WRITE_FIELD(this,
kInstanceDescriptorsOrBitField3Offset,
......
......@@ -279,12 +279,10 @@ void JSObject::PrintProperties(FILE* out) {
case CONSTANT_TRANSITION:
PrintF(out, "(constant transition)\n");
break;
case NULL_DESCRIPTOR:
PrintF(out, "(null descriptor)\n");
break;
case NORMAL: // only in slow mode
case HANDLER: // only in lookup results, not in descriptors
case INTERCEPTOR: // only in lookup results, not in descriptors
case NONEXISTENT:
UNREACHABLE();
break;
}
......
This diff is collapsed.
......@@ -2501,9 +2501,13 @@ class DescriptorArray: public FixedArray {
// Accessors for fetching instance descriptor at descriptor number.
inline String* GetKey(int descriptor_number);
inline Object** GetKeySlot(int descriptor_number);
inline void SetKeyUnchecked(Heap* heap, int descriptor_number, String* value);
inline Object* GetValue(int descriptor_number);
inline Object** GetValueSlot(int descriptor_number);
inline void SetNullValueUnchecked(int descriptor_number, Heap* heap);
inline void SetNullValueUnchecked(Heap* heap, int descriptor_number);
inline void SetValueUnchecked(Heap* heap,
int descriptor_number,
Object* value);
inline PropertyDetails GetDetails(int descriptor_number);
inline void SetDetailsUnchecked(int descriptor_number, Smi* value);
inline PropertyType GetType(int descriptor_number);
......@@ -2513,7 +2517,6 @@ class DescriptorArray: public FixedArray {
inline AccessorDescriptor* GetCallbacks(int descriptor_number);
inline bool IsProperty(int descriptor_number);
inline bool IsTransitionOnly(int descriptor_number);
inline bool IsNullDescriptor(int descriptor_number);
// WhitenessWitness is used to prove that a specific descriptor array is white
// (unmarked), so incremental write barriers can be skipped because the
......@@ -4780,6 +4783,10 @@ class Map: public HeapObject {
// [instance descriptors]: describes the object.
DECL_ACCESSORS(instance_descriptors, DescriptorArray)
// Should only be called to clear a descriptor array that was only used to
// store transitions and does not contain any live transitions anymore.
inline void ClearDescriptorArray();
// Sets the instance descriptor array for the map to be an empty descriptor
// array.
inline void clear_instance_descriptors();
......
......@@ -2220,7 +2220,9 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
case INTERCEPTOR: // only in lookup results, not in descriptors
case MAP_TRANSITION: // we do not care about transitions here...
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR: // ... and not about "holes"
break;
case NONEXISTENT:
UNREACHABLE();
break;
}
}
......
......@@ -64,7 +64,8 @@ enum PropertyType {
// All properties before MAP_TRANSITION are real.
MAP_TRANSITION = 6, // only in fast mode
CONSTANT_TRANSITION = 7, // only in fast mode
NULL_DESCRIPTOR = 8 // only in fast mode
// Only used as a marker in LookupResult.
NONEXISTENT = 8
};
......
......@@ -89,8 +89,8 @@ void LookupResult::Print(FILE* out) {
GetTransitionMap()->Print(out);
PrintF(out, "\n");
break;
case NULL_DESCRIPTOR:
PrintF(out, " =type = null descriptor\n");
case NONEXISTENT:
UNREACHABLE();
break;
}
}
......@@ -123,8 +123,10 @@ bool Descriptor::ContainsTransition() {
case CONSTANT_FUNCTION:
case HANDLER:
case INTERCEPTOR:
case NULL_DESCRIPTOR:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // Keep the compiler happy.
return false;
......
......@@ -173,8 +173,10 @@ bool IsPropertyDescriptor(T* desc) {
}
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
return false;
case NONEXISTENT:
UNREACHABLE();
break;
}
UNREACHABLE(); // keep the compiler happy
return false;
......@@ -189,7 +191,7 @@ class LookupResult BASE_EMBEDDED {
lookup_type_(NOT_FOUND),
holder_(NULL),
cacheable_(true),
details_(NONE, NULL_DESCRIPTOR) {
details_(NONE, NONEXISTENT) {
isolate->SetTopLookupResult(this);
}
......@@ -237,7 +239,7 @@ class LookupResult BASE_EMBEDDED {
void NotFound() {
lookup_type_ = NOT_FOUND;
details_ = PropertyDetails(NONE, NULL_DESCRIPTOR);
details_ = PropertyDetails(NONE, NONEXISTENT);
holder_ = NULL;
}
......
......@@ -10286,9 +10286,9 @@ static MaybeObject* DebugLookupResultValue(Heap* heap,
case INTERCEPTOR:
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
return heap->undefined_value();
case HANDLER:
case NONEXISTENT:
UNREACHABLE();
return heap->undefined_value();
}
......
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