Cache multiple ElementsKind map transition per map.

R=jkummerow@chromium.org
BUG=none
TEST=none

Review URL: http://codereview.chromium.org/8017003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9417 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
danno@chromium.org 2011-09-23 15:09:00 +00:00
parent 65b1ea22fe
commit d21902b25e
5 changed files with 202 additions and 68 deletions

View File

@ -1626,13 +1626,23 @@ void MarkCompactCollector::MarkDescriptorArray(
RecordSlot(slot, slot, *slot);
if (details.type() < FIRST_PHANTOM_PROPERTY_TYPE) {
PropertyType type = details.type();
if (type < FIRST_PHANTOM_PROPERTY_TYPE) {
HeapObject* object = HeapObject::cast(value);
MarkBit mark = Marking::MarkBitFrom(HeapObject::cast(object));
if (!mark.Get()) {
SetMark(HeapObject::cast(object), mark);
marking_deque_.PushBlack(object);
}
} else if (type == ELEMENTS_TRANSITION && value->IsFixedArray()) {
// For maps with multiple elements transitions, the transition maps are
// stored in a FixedArray. Keep the fixed array alive but not the maps
// that it refers to.
HeapObject* object = HeapObject::cast(value);
MarkBit mark = Marking::MarkBitFrom(HeapObject::cast(object));
if (!mark.Get()) {
SetMark(HeapObject::cast(object), mark);
}
}
}
// The DescriptorArray descriptors contains a pointer to its contents array,

View File

@ -315,12 +315,25 @@ void JSObject::PrintProperties(FILE* out) {
descs->GetCallbacksObject(i)->ShortPrint(out);
PrintF(out, " (callback)\n");
break;
case ELEMENTS_TRANSITION:
case ELEMENTS_TRANSITION: {
PrintF(out, "(elements transition to ");
PrintElementsKind(out,
Map::cast(descs->GetValue(i))->elements_kind());
Object* descriptor_contents = descs->GetValue(i);
if (descriptor_contents->IsMap()) {
Map* map = Map::cast(descriptor_contents);
PrintElementsKind(out, map->elements_kind());
} else {
FixedArray* map_array = FixedArray::cast(descriptor_contents);
for (int i = 0; i < map_array->length(); ++i) {
Map* map = Map::cast(map_array->get(i));
if (i != 0) {
PrintF(out, ", ");
}
PrintElementsKind(out, map->elements_kind());
}
}
PrintF(out, ")\n");
break;
}
case MAP_TRANSITION:
PrintF(out, "(map transition)\n");
break;

View File

@ -2022,6 +2022,84 @@ void Map::LookupInDescriptors(JSObject* holder,
}
static Map* GetElementsTransitionMapFromDescriptor(Object* descriptor_contents,
ElementsKind elements_kind) {
if (descriptor_contents->IsMap()) {
Map* map = Map::cast(descriptor_contents);
if (map->elements_kind() == elements_kind) {
return map;
}
return NULL;
}
FixedArray* map_array = FixedArray::cast(descriptor_contents);
for (int i = 0; i < map_array->length(); ++i) {
Object* current = map_array->get(i);
// Skip undefined slots, they are sentinels for reclaimed maps.
if (!current->IsUndefined()) {
Map* current_map = Map::cast(map_array->get(i));
if (current_map->elements_kind() == elements_kind) {
return current_map;
}
}
}
return NULL;
}
static MaybeObject* AddElementsTransitionMapToDescriptor(
Object* descriptor_contents,
Map* new_map) {
// Nothing was in the descriptor for an ELEMENTS_TRANSITION,
// simply add the map.
if (descriptor_contents == NULL) {
return new_map;
}
// There was already a map in the descriptor, create a 2-element FixedArray
// to contain the existing map plus the new one.
FixedArray* new_array;
Heap* heap = new_map->GetHeap();
if (descriptor_contents->IsMap()) {
// Must tenure, DescriptorArray expects no new-space objects.
MaybeObject* maybe_new_array = heap->AllocateFixedArray(2, TENURED);
if (!maybe_new_array->To<FixedArray>(&new_array)) {
return maybe_new_array;
}
new_array->set(0, descriptor_contents);
new_array->set(1, new_map);
return new_array;
}
// The descriptor already contained a list of maps for different ElementKinds
// of ELEMENTS_TRANSITION, first check the existing array for an undefined
// slot, and if that's not available, create a FixedArray to hold the existing
// maps plus the new one and fill it in.
FixedArray* array = FixedArray::cast(descriptor_contents);
for (int i = 0; i < array->length(); ++i) {
if (array->get(i)->IsUndefined()) {
array->set(i, new_map);
return array;
}
}
// Must tenure, DescriptorArray expects no new-space objects.
MaybeObject* maybe_new_array =
heap->AllocateFixedArray(array->length() + 1, TENURED);
if (!maybe_new_array->To<FixedArray>(&new_array)) {
return maybe_new_array;
}
int i = 0;
while (i < array->length()) {
new_array->set(i, array->get(i));
++i;
}
new_array->set(i, new_map);
return new_array;
}
MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
Heap* current_heap = GetHeap();
Map* current_map = map();
@ -2044,6 +2122,7 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
safe_to_add_transition = false;
}
Object* descriptor_contents = NULL;
if (safe_to_add_transition) {
// It's only safe to manipulate the descriptor array if it would be
// safe to add a transition.
@ -2063,9 +2142,15 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
// return it.
if (index != DescriptorArray::kNotFound) {
PropertyDetails details(PropertyDetails(descriptors->GetDetails(index)));
if (details.type() == ELEMENTS_TRANSITION &&
details.elements_kind() == elements_kind) {
return descriptors->GetValue(index);
if (details.type() == ELEMENTS_TRANSITION) {
descriptor_contents = descriptors->GetValue(index);
Map* maybe_transition_map =
GetElementsTransitionMapFromDescriptor(descriptor_contents,
elements_kind);
if (maybe_transition_map != NULL) {
ASSERT(maybe_transition_map->IsMap());
return maybe_transition_map;
}
} else {
safe_to_add_transition = false;
}
@ -2085,15 +2170,19 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
// Only remember the map transition if the object's map is NOT equal to the
// global object_function's map and there is not an already existing
// non-matching element transition.
bool allow_map_transition =
safe_to_add_transition &&
bool allow_map_transition = safe_to_add_transition &&
(GetIsolate()->context()->global_context()->object_function()->map() !=
map());
if (allow_map_transition) {
// Allocate new instance descriptors for the old map with map transition.
MaybeObject* maybe_new_contents =
AddElementsTransitionMapToDescriptor(descriptor_contents, new_map);
Object* new_contents;
if (!maybe_new_contents->ToObject(&new_contents)) {
return maybe_new_contents;
}
ElementsTransitionDescriptor desc(elements_transition_sentinel_name,
Map::cast(new_map),
elements_kind);
new_contents);
Object* new_descriptors;
MaybeObject* maybe_new_descriptors = descriptors->CopyInsert(
&desc,
@ -6384,29 +6473,45 @@ void String::PrintOn(FILE* file) {
}
void Map::CreateOneBackPointer(Map* target) {
#ifdef DEBUG
// Verify target.
Object* source_prototype = prototype();
Object* target_prototype = target->prototype();
ASSERT(source_prototype->IsJSReceiver() ||
source_prototype->IsMap() ||
source_prototype->IsNull());
ASSERT(target_prototype->IsJSReceiver() ||
target_prototype->IsNull());
ASSERT(source_prototype->IsMap() ||
source_prototype == target_prototype);
#endif
// Point target back to source. set_prototype() will not let us set
// the prototype to a map, as we do here.
*RawField(target, kPrototypeOffset) = this;
}
void Map::CreateBackPointers() {
DescriptorArray* descriptors = instance_descriptors();
for (int i = 0; i < descriptors->number_of_descriptors(); i++) {
if (descriptors->GetType(i) == MAP_TRANSITION ||
descriptors->GetType(i) == ELEMENTS_TRANSITION ||
descriptors->GetType(i) == CONSTANT_TRANSITION) {
// Get target.
Map* target = Map::cast(descriptors->GetValue(i));
#ifdef DEBUG
// Verify target.
Object* source_prototype = prototype();
Object* target_prototype = target->prototype();
ASSERT(source_prototype->IsJSReceiver() ||
source_prototype->IsMap() ||
source_prototype->IsNull());
ASSERT(target_prototype->IsJSReceiver() ||
target_prototype->IsNull());
ASSERT(source_prototype->IsMap() ||
source_prototype == target_prototype);
#endif
// Point target back to source. set_prototype() will not let us set
// the prototype to a map, as we do here.
*RawField(target, kPrototypeOffset) = this;
Object* object = reinterpret_cast<Object*>(descriptors->GetValue(i));
if (object->IsMap()) {
CreateOneBackPointer(reinterpret_cast<Map*>(object));
} else {
ASSERT(object->IsFixedArray());
ASSERT(descriptors->GetType(i) == ELEMENTS_TRANSITION);
FixedArray* array = reinterpret_cast<FixedArray*>(object);
for (int i = 0; i < array->length(); ++i) {
Map* target = reinterpret_cast<Map*>(array->get(i));
if (!target->IsUndefined()) {
CreateOneBackPointer(target);
}
}
}
}
}
}
@ -6433,17 +6538,46 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
if (details.type() == MAP_TRANSITION ||
details.type() == ELEMENTS_TRANSITION ||
details.type() == CONSTANT_TRANSITION) {
Map* target = reinterpret_cast<Map*>(contents->get(i));
ASSERT(target->IsHeapObject());
MarkBit map_mark = Marking::MarkBitFrom(target);
if (!map_mark.Get()) {
ASSERT(target->IsMap());
contents->set_unchecked(i + 1, NullDescriptorDetails);
contents->set_null_unchecked(heap, i);
ASSERT(target->prototype() == this ||
target->prototype() == real_prototype);
// Getter prototype() is read-only, set_prototype() has side effects.
*RawField(target, Map::kPrototypeOffset) = real_prototype;
Object* object = reinterpret_cast<Object*>(contents->get(i));
if (object->IsMap()) {
Map* target = reinterpret_cast<Map*>(object);
ASSERT(target->IsHeapObject());
MarkBit map_mark = Marking::MarkBitFrom(target);
if (!map_mark.Get()) {
ASSERT(target->IsMap());
contents->set_unchecked(i + 1, NullDescriptorDetails);
contents->set_null_unchecked(heap, i);
ASSERT(target->prototype() == this ||
target->prototype() == real_prototype);
// Getter prototype() is read-only, set_prototype() has side effects.
*RawField(target, Map::kPrototypeOffset) = real_prototype;
}
} else {
ASSERT(object->IsFixedArray());
ASSERT(details.type() == ELEMENTS_TRANSITION);
FixedArray* array = reinterpret_cast<FixedArray*>(contents->get(i));
bool reachable_map_found = false;
for (int j = 0; j < array->length(); ++j) {
Map* target = reinterpret_cast<Map*>(array->get(j));
ASSERT(target->IsHeapObject());
MarkBit map_mark = Marking::MarkBitFrom(target);
if (!map_mark.Get()) {
ASSERT(target->IsMap());
array->set_undefined(j);
ASSERT(target->prototype() == this ||
target->prototype() == real_prototype);
// Getter prototype() is read-only, set_prototype() has side
// effects.
*RawField(target, Map::kPrototypeOffset) = real_prototype;
} else {
reachable_map_found = true;
}
}
// If no map was found, make sure the FixedArray also gets collected.
if (!reachable_map_found) {
contents->set_unchecked(i + 1, NullDescriptorDetails);
contents->set_null_unchecked(heap, i);
}
}
}
}

View File

@ -180,7 +180,6 @@ class PropertyDetails BASE_EMBEDDED {
PropertyDetails(PropertyAttributes attributes,
PropertyType type,
int index = 0) {
ASSERT(type != ELEMENTS_TRANSITION);
ASSERT(TypeField::is_valid(type));
ASSERT(AttributesField::is_valid(attributes));
ASSERT(StorageField::is_valid(index));
@ -194,23 +193,6 @@ class PropertyDetails BASE_EMBEDDED {
ASSERT(index == this->index());
}
PropertyDetails(PropertyAttributes attributes,
PropertyType type,
ElementsKind elements_kind) {
ASSERT(type == ELEMENTS_TRANSITION);
ASSERT(TypeField::is_valid(type));
ASSERT(AttributesField::is_valid(attributes));
ASSERT(StorageField::is_valid(static_cast<int>(elements_kind)));
value_ = TypeField::encode(type)
| AttributesField::encode(attributes)
| StorageField::encode(static_cast<int>(elements_kind));
ASSERT(type == this->type());
ASSERT(attributes == this->attributes());
ASSERT(elements_kind == this->elements_kind());
}
// Conversion for storing details as Object*.
explicit inline PropertyDetails(Smi* smi);
inline Smi* AsSmi();
@ -232,11 +214,6 @@ class PropertyDetails BASE_EMBEDDED {
int index() { return StorageField::decode(value_); }
ElementsKind elements_kind() {
ASSERT(type() == ELEMENTS_TRANSITION);
return static_cast<ElementsKind>(StorageField::decode(value_));
}
inline PropertyDetails AsDeleted();
static bool IsValidIndex(int index) {
@ -4176,6 +4153,8 @@ class Map: public HeapObject {
// This is undone in MarkCompactCollector::ClearNonLiveTransitions().
void CreateBackPointers();
void CreateOneBackPointer(Map* transition_target);
// Set all map transitions from this map to dead maps to null.
// Also, restore the original prototype on the targets of these
// transitions, so that we do not process this map again while

View File

@ -115,11 +115,9 @@ class MapTransitionDescriptor: public Descriptor {
class ElementsTransitionDescriptor: public Descriptor {
public:
ElementsTransitionDescriptor(String* key,
Map* map,
ElementsKind elements_kind)
: Descriptor(key, map, PropertyDetails(NONE,
ELEMENTS_TRANSITION,
elements_kind)) { }
Object* map_or_array)
: Descriptor(key, map_or_array, PropertyDetails(NONE,
ELEMENTS_TRANSITION)) { }
};
// Marks a field name in a map so that adding the field is guaranteed