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 =
isolate->factory()->NewExternalArray(length, array_type, data);
// If the object already has external elements, create a new, unique
// map if the element type is now changing, because assumptions about
// generated code based on the receiver's map will be invalid.
i::Handle<i::HeapObject> elements(object->elements());
bool cant_reuse_map =
elements->map()->IsUndefined() ||
!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);
}
i::Handle<i::Map> external_array_map =
isolate->factory()->GetElementsTransitionMap(
object,
GetElementsKindFromExternalArrayType(array_type));
object->set_map(*external_array_map);
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> src,
ElementsKind elements_kind,
bool safe_to_add_transition) {
Handle<JSObject> src,
ElementsKind elements_kind) {
CALL_HEAP_FUNCTION(isolate(),
src->GetElementsTransitionMap(elements_kind,
safe_to_add_transition),
src->GetElementsTransitionMap(elements_kind),
Map);
}

View File

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

View File

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

View File

@ -1428,7 +1428,7 @@ void JSObject::initialize_elements() {
MaybeObject* JSObject::ResetElements() {
Object* obj;
{ MaybeObject* maybe_obj = map()->GetFastElementsMap();
{ MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_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() {
Object* object = READ_FIELD(this, kInstanceDescriptorsOrBitField3Offset);
if (object->IsSmi()) {

View File

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

View File

@ -1999,17 +1999,32 @@ void Map::LookupInDescriptors(JSObject* holder,
}
MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind,
bool safe_to_add_transition) {
Heap* current_heap = heap();
DescriptorArray* descriptors = instance_descriptors();
MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
Heap* current_heap = GetHeap();
Map* current_map = map();
DescriptorArray* descriptors = current_map->instance_descriptors();
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) {
// It's only safe to manipulate the descriptor array if it would be
// safe to add a transition.
ASSERT(!is_shared()); // no transitions can be added to shared maps.
// Check if the elements transition already exists.
DescriptorLookupCache* 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
// one.
Object* obj;
{ MaybeObject* maybe_map = CopyDropTransitions();
{ MaybeObject* maybe_map = current_map->CopyDropTransitions();
if (!maybe_map->ToObject(&obj)) return maybe_map;
}
Map* new_map = Map::cast(obj);
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
// 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;
}
descriptors = DescriptorArray::cast(new_descriptors);
set_instance_descriptors(descriptors);
current_map->set_instance_descriptors(descriptors);
}
return new_map;
@ -2973,7 +2987,7 @@ MaybeObject* JSObject::NormalizeElements() {
// Set the new map first to satify the elements type assert in
// set_elements().
Object* new_map;
MaybeObject* maybe = map()->GetSlowElementsMap();
MaybeObject* maybe = GetElementsTransitionMap(DICTIONARY_ELEMENTS);
if (!maybe->ToObject(&new_map)) return maybe;
set_map(Map::cast(new_map));
set_elements(dictionary);
@ -7281,7 +7295,7 @@ MaybeObject* JSObject::SetFastElementsCapacityAndLength(int capacity,
Map* new_map = NULL;
if (elements()->map() != heap->non_strict_arguments_elements_map()) {
Object* object;
MaybeObject* maybe = map()->GetFastElementsMap();
MaybeObject* maybe = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe->ToObject(&object)) return maybe;
new_map = Map::cast(object);
}
@ -7384,7 +7398,8 @@ MaybeObject* JSObject::SetFastDoubleElementsCapacityAndLength(
}
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;
}
Map* new_map = Map::cast(obj);
@ -10141,7 +10156,7 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) {
// Convert to fast elements.
Object* obj;
{ MaybeObject* maybe_obj = map()->GetFastElementsMap();
{ MaybeObject* maybe_obj = GetElementsTransitionMap(FAST_ELEMENTS);
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
}
Map* new_map = Map::cast(obj);

View File

@ -1860,6 +1860,11 @@ class JSObject: public JSReceiver {
Object* value,
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,
// backed by the properties array. Descriptors of visible
// types, such as CONSTANT_FUNCTION, keep their enumeration order.
@ -4138,27 +4143,6 @@ class Map: public HeapObject {
// instance descriptors.
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).
int PropertyIndexFor(String* name);

View File

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

View File

@ -126,10 +126,6 @@ namespace internal {
V8.GCCompactorCausedByWeakHandles) \
SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
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? */ \
SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \