Add prototype transitions cache to Map.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7679 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2011-04-26 09:44:55 +00:00
parent bde82b06c0
commit 74c258f281
6 changed files with 143 additions and 7 deletions

View File

@ -223,6 +223,8 @@ DEFINE_bool(use_flow_graph, false, "perform flow-graph based optimizations")
// compilation-cache.cc
DEFINE_bool(compilation_cache, true, "enable compilation cache")
DEFINE_bool(cache_prototype_transitions, true, "cache prototype transitions")
// data-flow.cc
DEFINE_bool(loop_peeling, false, "Peel off the first iteration of loops.")

View File

@ -1594,6 +1594,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) {
map->set_pre_allocated_property_fields(0);
map->set_instance_descriptors(empty_descriptor_array());
map->set_code_cache(empty_fixed_array());
map->set_prototype_transitions(empty_fixed_array());
map->set_unused_property_fields(0);
map->set_bit_field(0);
map->set_bit_field2((1 << Map::kIsExtensible) | (1 << Map::kHasFastElements));
@ -1686,12 +1687,15 @@ bool Heap::CreateInitialMaps() {
// Fix the instance_descriptors for the existing maps.
meta_map()->set_instance_descriptors(empty_descriptor_array());
meta_map()->set_code_cache(empty_fixed_array());
meta_map()->set_prototype_transitions(empty_fixed_array());
fixed_array_map()->set_instance_descriptors(empty_descriptor_array());
fixed_array_map()->set_code_cache(empty_fixed_array());
fixed_array_map()->set_prototype_transitions(empty_fixed_array());
oddball_map()->set_instance_descriptors(empty_descriptor_array());
oddball_map()->set_code_cache(empty_fixed_array());
oddball_map()->set_prototype_transitions(empty_fixed_array());
// Fix prototype object for existing maps.
meta_map()->set_prototype(null_value());

View File

@ -1077,6 +1077,12 @@ void MarkCompactCollector::MarkUnmarkedObject(HeapObject* object) {
void MarkCompactCollector::MarkMapContents(Map* map) {
// Mark prototype transitions array but don't push it into marking stack.
// This will make references from it weak. We will clean dead prototype
// transitions in ClearNonLiveTransitions.
FixedArray* prototype_transitions = map->unchecked_prototype_transitions();
if (!prototype_transitions->IsMarked()) SetMark(prototype_transitions);
MarkDescriptorArray(reinterpret_cast<DescriptorArray*>(
*HeapObject::RawField(map, Map::kInstanceDescriptorsOffset)));
@ -1494,7 +1500,7 @@ bool MarkCompactCollector::SafeIsMap(HeapObject* object) {
void MarkCompactCollector::ClearNonLiveTransitions() {
HeapObjectIterator map_iterator(heap() ->map_space(), &SizeOfMarkedObject);
HeapObjectIterator map_iterator(heap()->map_space(), &SizeOfMarkedObject);
// Iterate over the map space, setting map transitions that go from
// a marked map to an unmarked map to null transitions. At the same time,
// set all the prototype fields of maps back to their original value,
@ -1522,6 +1528,41 @@ void MarkCompactCollector::ClearNonLiveTransitions() {
map->unchecked_constructor()->unchecked_shared()->AttachInitialMap(map);
}
// Clear dead prototype transitions.
FixedArray* prototype_transitions = map->unchecked_prototype_transitions();
if (prototype_transitions->length() > 0) {
int finger = Smi::cast(prototype_transitions->get(0))->value();
int new_finger = 1;
for (int i = 1; i < finger; i += 2) {
Object* prototype = prototype_transitions->get(i);
Object* cached_map = prototype_transitions->get(i + 1);
if (HeapObject::cast(prototype)->IsMarked() &&
HeapObject::cast(cached_map)->IsMarked()) {
if (new_finger != i) {
prototype_transitions->set_unchecked(heap_,
new_finger,
prototype,
UPDATE_WRITE_BARRIER);
prototype_transitions->set_unchecked(heap_,
new_finger + 1,
cached_map,
SKIP_WRITE_BARRIER);
}
new_finger += 2;
}
}
// Fill slots that became free with undefined value.
Object* undefined = heap()->raw_unchecked_undefined_value();
for (int i = new_finger; i < finger; i++) {
prototype_transitions->set_unchecked(heap_,
i,
undefined,
SKIP_WRITE_BARRIER);
}
prototype_transitions->set_unchecked(0, Smi::FromInt(new_finger));
}
// Follow the chain of back pointers to find the prototype.
Map* current = map;
while (SafeIsMap(current)) {

View File

@ -2537,6 +2537,12 @@ JSFunction* Map::unchecked_constructor() {
}
FixedArray* Map::unchecked_prototype_transitions() {
return reinterpret_cast<FixedArray*>(
READ_FIELD(this, kPrototypeTransitionsOffset));
}
Code::Flags Code::flags() {
return static_cast<Flags>(READ_INT_FIELD(this, kFlagsOffset));
}
@ -2945,6 +2951,7 @@ MaybeObject* Map::GetSlowElementsMap() {
ACCESSORS(Map, instance_descriptors, DescriptorArray,
kInstanceDescriptorsOffset)
ACCESSORS(Map, code_cache, Object, kCodeCacheOffset)
ACCESSORS(Map, prototype_transitions, FixedArray, kPrototypeTransitionsOffset)
ACCESSORS(Map, constructor, Object, kConstructorOffset)
ACCESSORS(JSFunction, shared, SharedFunctionInfo, kSharedFunctionInfoOffset)

View File

@ -6883,6 +6883,49 @@ MaybeObject* JSObject::SetElementsLength(Object* len) {
}
Object* Map::GetPrototypeTransition(Object* prototype) {
FixedArray* cache = prototype_transitions();
int capacity = cache->length();
if (capacity == 0) return NULL;
int finger = Smi::cast(cache->get(0))->value();
for (int i = 1; i < finger; i += 2) {
if (cache->get(i) == prototype) return cache->get(i + 1);
}
return NULL;
}
MaybeObject* Map::PutPrototypeTransition(Object* prototype, Map* map) {
// Don't cache prototype transition if this map is shared.
if (is_shared() || !FLAG_cache_prototype_transitions) return this;
FixedArray* cache = prototype_transitions();
int capacity = cache->length();
int finger = (capacity == 0) ? 1 : Smi::cast(cache->get(0))->value();
if (finger >= capacity) {
if (capacity > kMaxCachedPrototypeTransitions) return this;
FixedArray* new_cache;
{ MaybeObject* maybe_cache = heap()->AllocateFixedArray(finger * 2 + 1);
if (!maybe_cache->To<FixedArray>(&new_cache)) return maybe_cache;
}
for (int i = 1; i < capacity; i++) new_cache->set(i, cache->get(i));
cache = new_cache;
set_prototype_transitions(cache);
}
cache->set(finger, prototype);
cache->set(finger + 1, map);
cache->set(0, Smi::FromInt(finger + 2));
return cache;
}
MaybeObject* JSObject::SetPrototype(Object* value,
bool skip_hidden_prototypes) {
Heap* heap = GetHeap();
@ -6933,11 +6976,25 @@ MaybeObject* JSObject::SetPrototype(Object* value,
}
// Set the new prototype of the object.
Object* new_map;
{ MaybeObject* maybe_new_map = real_receiver->map()->CopyDropTransitions();
if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
Map* map = real_receiver->map();
// Nothing to do if prototype is already set.
if (map->prototype() == value) return value;
Object* new_map = map->GetPrototypeTransition(value);
if (new_map == NULL) {
{ MaybeObject* maybe_new_map = map->CopyDropTransitions();
if (!maybe_new_map->ToObject(&new_map)) return maybe_new_map;
}
{ MaybeObject* maybe_new_cache =
map->PutPrototypeTransition(value, Map::cast(new_map));
if (maybe_new_cache->IsFailure()) return maybe_new_cache;
}
Map::cast(new_map)->set_prototype(value);
}
Map::cast(new_map)->set_prototype(value);
ASSERT(Map::cast(new_map)->prototype() == value);
real_receiver->set_map(Map::cast(new_map));
heap->ClearInstanceofCache();

View File

@ -649,6 +649,13 @@ class MaybeObject BASE_EMBEDDED {
return reinterpret_cast<Object*>(this);
}
template<typename T>
inline bool To(T** obj) {
if (IsFailure()) return false;
*obj = T::cast(reinterpret_cast<Object*>(this));
return true;
}
#ifdef OBJECT_PRINT
// Prints this object with details.
inline void Print() {
@ -3744,6 +3751,16 @@ class Map: public HeapObject {
// [stub cache]: contains stubs compiled for this map.
DECL_ACCESSORS(code_cache, Object)
// [prototype transitions]: cache of prototype transitions.
// Prototype transition is a transition that happens
// when we change object's prototype to a new one.
// Cache format:
// 0: finger - index of the first free cell in the cache
// 1 + 2 * i: prototype
// 2 + 2 * i: target map
DECL_ACCESSORS(prototype_transitions, FixedArray)
inline FixedArray* unchecked_prototype_transitions();
// Lookup in the map's instance descriptors and fill out the result
// with the given holder if the name is found. The holder may be
// NULL when this function is used from the compiler.
@ -3843,6 +3860,12 @@ class Map: public HeapObject {
void TraverseTransitionTree(TraverseCallback callback, void* data);
static const int kMaxCachedPrototypeTransitions = 256;
Object* GetPrototypeTransition(Object* prototype);
MaybeObject* PutPrototypeTransition(Object* prototype, Map* map);
static const int kMaxPreAllocatedPropertyFields = 255;
// Layout description.
@ -3853,14 +3876,16 @@ class Map: public HeapObject {
static const int kInstanceDescriptorsOffset =
kConstructorOffset + kPointerSize;
static const int kCodeCacheOffset = kInstanceDescriptorsOffset + kPointerSize;
static const int kPadStart = kCodeCacheOffset + kPointerSize;
static const int kPrototypeTransitionsOffset =
kCodeCacheOffset + kPointerSize;
static const int kPadStart = kPrototypeTransitionsOffset + kPointerSize;
static const int kSize = MAP_POINTER_ALIGN(kPadStart);
// Layout of pointer fields. Heap iteration code relies on them
// being continiously allocated.
static const int kPointerFieldsBeginOffset = Map::kPrototypeOffset;
static const int kPointerFieldsEndOffset =
Map::kCodeCacheOffset + kPointerSize;
Map::kPrototypeTransitionsOffset + kPointerSize;
// Byte offsets within kInstanceSizesOffset.
static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;