Share Maps for ElementsKind transitions

Support sharing maps after an changing an object's ElementsKind for element kinds other then external elements.

R=svenpanne@chromium.org
BUG=none
TEST=external-arrays.js

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9304 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
danno@chromium.org 2011-09-15 16:38:47 +00:00
parent 7380670755
commit 6a5e0448c8
10 changed files with 58 additions and 122 deletions

View File

@ -3310,22 +3310,12 @@ void PrepareExternalArrayElements(i::Handle<i::JSObject> object,
i::Handle<i::ExternalArray> array = i::Handle<i::ExternalArray> array =
isolate->factory()->NewExternalArray(length, array_type, data); isolate->factory()->NewExternalArray(length, array_type, data);
// If the object already has external elements, create a new, unique i::Handle<i::Map> external_array_map =
// map if the element type is now changing, because assumptions about isolate->factory()->GetElementsTransitionMap(
// generated code based on the receiver's map will be invalid. object,
i::Handle<i::HeapObject> elements(object->elements()); GetElementsKindFromExternalArrayType(array_type));
bool cant_reuse_map =
elements->map()->IsUndefined() || object->set_map(*external_array_map);
!elements->map()->has_external_array_elements() ||
elements->map() != isolate->heap()->MapForExternalArrayType(array_type);
if (cant_reuse_map) {
i::Handle<i::Map> external_array_map =
isolate->factory()->GetElementsTransitionMap(
i::Handle<i::Map>(object->map()),
GetElementsKindFromExternalArrayType(array_type),
object->HasFastProperties());
object->set_map(*external_array_map);
}
object->set_elements(*array); object->set_elements(*array);
} }

View File

@ -455,23 +455,11 @@ Handle<Map> Factory::CopyMapDropTransitions(Handle<Map> src) {
} }
Handle<Map> Factory::GetFastElementsMap(Handle<Map> src) {
CALL_HEAP_FUNCTION(isolate(), src->GetFastElementsMap(), Map);
}
Handle<Map> Factory::GetSlowElementsMap(Handle<Map> src) {
CALL_HEAP_FUNCTION(isolate(), src->GetSlowElementsMap(), Map);
}
Handle<Map> Factory::GetElementsTransitionMap( Handle<Map> Factory::GetElementsTransitionMap(
Handle<Map> src, Handle<JSObject> src,
ElementsKind elements_kind, ElementsKind elements_kind) {
bool safe_to_add_transition) {
CALL_HEAP_FUNCTION(isolate(), CALL_HEAP_FUNCTION(isolate(),
src->GetElementsTransitionMap(elements_kind, src->GetElementsTransitionMap(elements_kind),
safe_to_add_transition),
Map); Map);
} }

View File

@ -215,13 +215,8 @@ class Factory {
Handle<Map> CopyMapDropTransitions(Handle<Map> map); Handle<Map> CopyMapDropTransitions(Handle<Map> map);
Handle<Map> GetFastElementsMap(Handle<Map> map); Handle<Map> GetElementsTransitionMap(Handle<JSObject> object,
ElementsKind elements_kind);
Handle<Map> GetSlowElementsMap(Handle<Map> map);
Handle<Map> GetElementsTransitionMap(Handle<Map> map,
ElementsKind elements_kind,
bool safe_to_add_transition);
Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array); Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array);

View File

@ -3951,14 +3951,14 @@ HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
bool is_store) { bool is_store) {
ASSERT(expr->IsMonomorphic()); ASSERT(expr->IsMonomorphic());
Handle<Map> map = expr->GetMonomorphicReceiverType(); Handle<Map> map = expr->GetMonomorphicReceiverType();
AddInstruction(new(zone()) HCheckNonSmi(object));
HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
if (!map->has_fast_elements() && if (!map->has_fast_elements() &&
!map->has_fast_double_elements() && !map->has_fast_double_elements() &&
!map->has_external_array_elements()) { !map->has_external_array_elements()) {
return is_store ? BuildStoreKeyedGeneric(object, key, val) return is_store ? BuildStoreKeyedGeneric(object, key, val)
: BuildLoadKeyedGeneric(object, key); : BuildLoadKeyedGeneric(object, key);
} }
AddInstruction(new(zone()) HCheckNonSmi(object));
HInstruction* mapcheck = AddInstruction(new(zone()) HCheckMap(object, map));
HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object)); HInstruction* elements = AddInstruction(new(zone()) HLoadElements(object));
bool fast_double_elements = map->has_fast_double_elements(); bool fast_double_elements = map->has_fast_double_elements();
if (is_store && map->has_fast_elements()) { if (is_store && map->has_fast_elements()) {

View File

@ -1428,7 +1428,7 @@ void JSObject::initialize_elements() {
MaybeObject* JSObject::ResetElements() { MaybeObject* JSObject::ResetElements() {
Object* obj; Object* obj;
{ MaybeObject* maybe_obj = map()->GetFastElementsMap(); { MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj; if (!maybe_obj->ToObject(&obj)) return maybe_obj;
} }
set_map(Map::cast(obj)); set_map(Map::cast(obj));
@ -3242,45 +3242,6 @@ void Map::set_prototype(Object* value, WriteBarrierMode mode) {
} }
MaybeObject* Map::GetFastElementsMap() {
if (has_fast_elements()) return this;
Object* obj;
{ MaybeObject* maybe_obj = CopyDropTransitions();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);
new_map->set_elements_kind(FAST_ELEMENTS);
isolate()->counters()->map_to_fast_elements()->Increment();
return new_map;
}
MaybeObject* Map::GetFastDoubleElementsMap() {
if (has_fast_double_elements()) return this;
Object* obj;
{ MaybeObject* maybe_obj = CopyDropTransitions();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);
new_map->set_elements_kind(FAST_DOUBLE_ELEMENTS);
isolate()->counters()->map_to_fast_double_elements()->Increment();
return new_map;
}
MaybeObject* Map::GetSlowElementsMap() {
if (!has_fast_elements() && !has_fast_double_elements()) return this;
Object* obj;
{ MaybeObject* maybe_obj = CopyDropTransitions();
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);
new_map->set_elements_kind(DICTIONARY_ELEMENTS);
isolate()->counters()->map_to_slow_elements()->Increment();
return new_map;
}
DescriptorArray* Map::instance_descriptors() { DescriptorArray* Map::instance_descriptors() {
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset); Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (object->IsSmi()) { if (object->IsSmi()) {

View File

@ -256,6 +256,9 @@ void JSObject::PrintProperties(FILE* out) {
descs->GetCallbacksObject(i)->ShortPrint(out); descs->GetCallbacksObject(i)->ShortPrint(out);
PrintF(out, " (callback)\n"); PrintF(out, " (callback)\n");
break; break;
case ELEMENTS_TRANSITION:
PrintF(out, " (elements transition)\n");
break;
case MAP_TRANSITION: case MAP_TRANSITION:
PrintF(out, " (map transition)\n"); PrintF(out, " (map transition)\n");
break; break;

View File

@ -1999,17 +1999,32 @@ void Map::LookupInDescriptors(JSObject* holder,
} }
MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind, MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
bool safe_to_add_transition) { Heap* current_heap = GetHeap();
Heap* current_heap = heap(); Map* current_map = map();
DescriptorArray* descriptors = instance_descriptors(); DescriptorArray* descriptors = current_map->instance_descriptors();
String* elements_transition_sentinel_name = current_heap->empty_symbol(); String* elements_transition_sentinel_name = current_heap->empty_symbol();
if (current_map->elements_kind() == elements_kind) return current_map;
// Only objects with FastProperties can have DescriptorArrays and can track
// element-related maps. Also don't add descriptors to maps that are shared.
bool safe_to_add_transition = HasFastProperties() &&
!current_map->IsUndefined() &&
!current_map->is_shared();
// Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps cause by objects
// with elements that switch back and forth between dictionary and fast
// element mode.
if ((current_map->elements_kind() == DICTIONARY_ELEMENTS &&
elements_kind == FAST_ELEMENTS)) {
safe_to_add_transition = false;
}
if (safe_to_add_transition) { if (safe_to_add_transition) {
// It's only safe to manipulate the descriptor array if it would be // It's only safe to manipulate the descriptor array if it would be
// safe to add a transition. // safe to add a transition.
ASSERT(!is_shared()); // no transitions can be added to shared maps.
// Check if the elements transition already exists. // Check if the elements transition already exists.
DescriptorLookupCache* cache = DescriptorLookupCache* cache =
current_heap->isolate()->descriptor_lookup_cache(); current_heap->isolate()->descriptor_lookup_cache();
@ -2037,13 +2052,12 @@ MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind,
// No transition to an existing map for the given ElementsKind. Make a new // No transition to an existing map for the given ElementsKind. Make a new
// one. // one.
Object* obj; Object* obj;
{ MaybeObject* maybe_map = CopyDropTransitions(); { MaybeObject* maybe_map = current_map->CopyDropTransitions();
if (!maybe_map->ToObject(&obj)) return maybe_map; if (!maybe_map->ToObject(&obj)) return maybe_map;
} }
Map* new_map = Map::cast(obj); Map* new_map = Map::cast(obj);
new_map->set_elements_kind(elements_kind); new_map->set_elements_kind(elements_kind);
GetIsolate()->counters()->map_to_external_array_elements()->Increment();
// Only remember the map transition if the object's map is NOT equal to the // 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 // global object_function's map and there is not an already existing
@ -2065,7 +2079,7 @@ MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind,
return maybe_new_descriptors; return maybe_new_descriptors;
} }
descriptors = DescriptorArray::cast(new_descriptors); descriptors = DescriptorArray::cast(new_descriptors);
set_instance_descriptors(descriptors); current_map->set_instance_descriptors(descriptors);
} }
return new_map; return new_map;
@ -2973,7 +2987,7 @@ MaybeObject* JSObject::NormalizeElements() {
// Set the new map first to satify the elements type assert in // Set the new map first to satify the elements type assert in
// set_elements(). // set_elements().
Object* new_map; Object* new_map;
MaybeObject* maybe = map()->GetSlowElementsMap(); MaybeObject* maybe = GetElementsTransitionMap(DICTIONARY_ELEMENTS);
if (!maybe->ToObject(&new_map)) return maybe; if (!maybe->ToObject(&new_map)) return maybe;
set_map(Map::cast(new_map)); set_map(Map::cast(new_map));
set_elements(dictionary); set_elements(dictionary);
@ -7281,7 +7295,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
Map* new_map = NULL; Map* new_map = NULL;
if (elements()->map() != heap->non_strict_arguments_elements_map()) { if (elements()->map() != heap->non_strict_arguments_elements_map()) {
Object* object; Object* object;
MaybeObject* maybe = map()->GetFastElementsMap(); MaybeObject* maybe = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe->ToObject(&object)) return maybe; if (!maybe->ToObject(&object)) return maybe;
new_map = Map::cast(object); new_map = Map::cast(object);
} }
@ -7384,7 +7398,8 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
} }
FixedDoubleArray* elems = FixedDoubleArray::cast(obj); FixedDoubleArray* elems = FixedDoubleArray::cast(obj);
{ MaybeObject* maybe_obj = map()->GetFastDoubleElementsMap(); { MaybeObject* maybe_obj =
GetElementsTransitionMap(FAST_DOUBLE_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj; if (!maybe_obj->ToObject(&obj)) return maybe_obj;
} }
Map* new_map = Map::cast(obj); Map* new_map = Map::cast(obj);
@ -10141,7 +10156,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
// Convert to fast elements. // Convert to fast elements.
Object* obj; Object* obj;
{ MaybeObject* maybe_obj = map()->GetFastElementsMap(); { MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj; if (!maybe_obj->ToObject(&obj)) return maybe_obj;
} }
Map* new_map = Map::cast(obj); Map* new_map = Map::cast(obj);

View File

@ -1860,6 +1860,11 @@ class JSObject: public JSReceiver {
Object* value, Object* value,
PropertyAttributes attributes); PropertyAttributes attributes);
// Returns a new map with all transitions dropped from the object's current
// map and the ElementsKind set.
MUST_USE_RESULT MaybeObject* GetElementsTransitionMap(
ElementsKind elements_kind);
// Converts a descriptor of any other type to a real field, // Converts a descriptor of any other type to a real field,
// backed by the properties array. Descriptors of visible // backed by the properties array. Descriptors of visible
// types, such as CONSTANT_FUNCTION, keep their enumeration order. // types, such as CONSTANT_FUNCTION, keep their enumeration order.
@ -4138,27 +4143,6 @@ class Map: public HeapObject {
// instance descriptors. // instance descriptors.
MUST_USE_RESULT MaybeObject* CopyDropTransitions(); MUST_USE_RESULT MaybeObject* CopyDropTransitions();
// Returns this map if it already has elements that are fast, otherwise
// returns a copy of the map, with all transitions dropped from the
// descriptors and the ElementsKind set to FAST_ELEMENTS.
MUST_USE_RESULT inline MaybeObject* GetFastElementsMap();
// Returns this map if it already has fast elements that are doubles,
// otherwise returns a copy of the map, with all transitions dropped from the
// descriptors and the ElementsKind set to FAST_DOUBLE_ELEMENTS.
MUST_USE_RESULT inline MaybeObject* GetFastDoubleElementsMap();
// Returns this map if already has dictionary elements, otherwise returns a
// copy of the map, with all transitions dropped from the descriptors and the
// ElementsKind set to DICTIONARY_ELEMENTS.
MUST_USE_RESULT inline MaybeObject* GetSlowElementsMap();
// Returns a new map with all transitions dropped from the descriptors and the
// ElementsKind set.
MUST_USE_RESULT MaybeObject* GetElementsTransitionMap(
ElementsKind elements_kind,
bool safe_to_add_transition);
// Returns the property index for name (only valid for FAST MODE). // Returns the property index for name (only valid for FAST MODE).
int PropertyIndexFor(String* name); int PropertyIndexFor(String* name);

View File

@ -9414,9 +9414,11 @@ class ArrayConcatVisitor {
isolate_->factory()->NewNumber(static_cast<double>(index_offset_)); isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
Handle<Map> map; Handle<Map> map;
if (fast_elements_) { if (fast_elements_) {
map = isolate_->factory()->GetFastElementsMap(Handle<Map>(array->map())); map = isolate_->factory()->GetElementsTransitionMap(array,
FAST_ELEMENTS);
} else { } else {
map = isolate_->factory()->GetSlowElementsMap(Handle<Map>(array->map())); map = isolate_->factory()->GetElementsTransitionMap(array,
DICTIONARY_ELEMENTS);
} }
array->set_map(*map); array->set_map(*map);
array->set_length(*length); array->set_length(*length);
@ -9907,15 +9909,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_MoveArrayContents) {
CONVERT_CHECKED(JSArray, to, args[1]); CONVERT_CHECKED(JSArray, to, args[1]);
FixedArrayBase* new_elements = from->elements(); FixedArrayBase* new_elements = from->elements();
MaybeObject* maybe_new_map; MaybeObject* maybe_new_map;
ElementsKind elements_kind;
if (new_elements->map() == isolate->heap()->fixed_array_map() || if (new_elements->map() == isolate->heap()->fixed_array_map() ||
new_elements->map() == isolate->heap()->fixed_cow_array_map()) { new_elements->map() == isolate->heap()->fixed_cow_array_map()) {
maybe_new_map = to->map()->GetFastElementsMap(); elements_kind = FAST_ELEMENTS;
} else if (new_elements->map() == } else if (new_elements->map() ==
isolate->heap()->fixed_double_array_map()) { isolate->heap()->fixed_double_array_map()) {
maybe_new_map = to->map()->GetFastDoubleElementsMap(); elements_kind = FAST_DOUBLE_ELEMENTS;
} else { } else {
maybe_new_map = to->map()->GetSlowElementsMap(); elements_kind = DICTIONARY_ELEMENTS;
} }
maybe_new_map = to->GetElementsTransitionMap(elements_kind);
Object* new_map; Object* new_map;
if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map; if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
to->set_map(Map::cast(new_map)); to->set_map(Map::cast(new_map));

View File

@ -126,10 +126,6 @@ namespace internal {
V8.GCCompactorCausedByWeakHandles) \ V8.GCCompactorCausedByWeakHandles) \
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \ SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \ SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
SC(map_to_fast_elements, V8.MapToFastElements) \
SC(map_to_fast_double_elements, V8.MapToFastDoubleElements) \
SC(map_to_slow_elements, V8.MapToSlowElements) \
SC(map_to_external_array_elements, V8.MapToExternalArrayElements) \
/* How is the generic keyed-load stub used? */ \ /* How is the generic keyed-load stub used? */ \
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \ SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \