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:
parent
7380670755
commit
6a5e0448c8
22
src/api.cc
22
src/api.cc
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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) \
|
||||
|
Loading…
Reference in New Issue
Block a user