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:
parent
bde82b06c0
commit
74c258f281
@ -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.")
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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)) {
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user