From 950ad34a94bb868aea34b1f5a5f5aa18c2c7980a Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Thu, 18 Sep 2008 06:55:14 +0000 Subject: [PATCH] Propagate the information that a monomorphic prototype failure has occured. After a monomorphic prototype failure has been detected, other monomorphic inline caches for the same type will have a code object in the map's code cache which is not the current target. We recognize this case and use the code object that is in the cache instead of going megamorphic. Review URL: http://codereview.chromium.org/2928 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@331 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ic.cc | 205 ++++++++++++++++++++++++++++-------------------------- src/ic.h | 26 ++++--- src/objects-inl.h | 14 ++++ src/objects.cc | 28 ++------ src/objects.h | 10 +-- 5 files changed, 146 insertions(+), 137 deletions(-) diff --git a/src/ic.cc b/src/ic.cc index 59794ea..2fdfb4f 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -61,7 +61,7 @@ void IC::TraceIC(const char* type, State old_state, Code* new_target) { if (FLAG_trace_ic) { - State new_state = StateFrom(new_target, Heap::undefined_value()); + State new_state = new_target->ic_state(); PrintF("[%s (%c->%c) ", type, TransitionMarkFromState(old_state), TransitionMarkFromState(new_state)); @@ -127,11 +127,16 @@ Address IC::OriginalCodeAddress() { } -IC::State IC::StateFrom(Code* target, Object* receiver) { - IC::State state = target->ic_state(); +IC::State IC::ComputeCacheState(Code* target, Object* receiver, + Object* name, Object** new_target) { + // Get the raw state from the target. + State target_state = target->ic_state(); - if (state != MONOMORPHIC) return state; - if (receiver->IsUndefined() || receiver->IsNull()) return state; + // Assume that no new target exists in the cache. + *new_target = Heap::undefined_value(); + + if (target_state != MONOMORPHIC) return target_state; + if (receiver->IsUndefined() || receiver->IsNull()) return target_state; Map* map = GetCodeCacheMapForObject(receiver); @@ -143,8 +148,14 @@ IC::State IC::StateFrom(Code* target, Object* receiver) { // the receiver map's code cache. Therefore, if the current target // is in the receiver map's code cache, the inline cache failed due // to prototype check failure. - int index = map->IndexInCodeCache(target); - if (index >= 0) { + int index = -1; + Object* code = NULL; + if (name->IsString()) { + code = map->FindIndexInCodeCache(String::cast(name), + target->flags(), + &index); + } + if (index >= 0 && code == target) { // For keyed load/store, the most likely cause of cache failure is // that the key has changed. We do not distinguish between // prototype and non-prototype failures for keyed access. @@ -172,6 +183,7 @@ IC::State IC::StateFrom(Code* target, Object* receiver) { return UNINITIALIZED; } + *new_target = code; return MONOMORPHIC; } @@ -275,8 +287,7 @@ Object* CallIC::TryCallAsFunction(Object* object) { } -Object* CallIC::LoadFunction(State state, - Handle object, +Object* CallIC::LoadFunction(Handle object, Handle name) { // If the object is undefined or null it's illegal to try to get any // of its properties; throw a TypeError in that case. @@ -315,7 +326,7 @@ Object* CallIC::LoadFunction(State state, // Lookup is valid: Update inline cache and stub cache. if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, object, name); + UpdateCaches(&lookup, object, name); } if (lookup.type() == INTERCEPTOR) { @@ -374,7 +385,6 @@ Object* CallIC::LoadFunction(State state, void CallIC::UpdateCaches(LookupResult* lookup, - State state, Handle object, Handle name) { ASSERT(lookup->IsLoaded()); @@ -383,52 +393,55 @@ void CallIC::UpdateCaches(LookupResult* lookup, // Compute the number of arguments. int argc = target()->arguments_count(); - Object* code = NULL; - if (state == UNINITIALIZED) { - // This is the first time we execute this inline cache. - // Set the target to the pre monomorphic stub to delay - // setting the monomorphic state. - code = StubCache::ComputeCallPreMonomorphic(argc); - } else if (state == MONOMORPHIC) { - code = StubCache::ComputeCallMegamorphic(argc); - } else { - // Compute monomorphic stub. - switch (lookup->type()) { - case FIELD: { - int index = lookup->GetFieldIndex(); - code = StubCache::ComputeCallField(argc, *name, *object, - lookup->holder(), index); - break; - } - case CONSTANT_FUNCTION: { - // Get the constant function and compute the code stub for this - // call; used for rewriting to monomorphic state and making sure - // that the code stub is in the stub cache. - JSFunction* function = lookup->GetConstantFunction(); - code = StubCache::ComputeCallConstant(argc, *name, *object, - lookup->holder(), function); - break; - } - case NORMAL: { - // There is only one shared stub for calling normalized - // properties. It does not traverse the prototype chain, so the - // property must be found in the receiver for the stub to be - // applicable. - if (!object->IsJSObject()) return; - Handle receiver = Handle::cast(object); - if (lookup->holder() != *receiver) return; - code = StubCache::ComputeCallNormal(argc, *name, *receiver); - break; - } - case INTERCEPTOR: { - code = StubCache::ComputeCallInterceptor(argc, *name, *object, - lookup->holder()); - break; + Object* code = Heap::undefined_value(); + State state = ComputeCacheState(target(), *object, *name, &code); + + switch (state) { + case UNINITIALIZED: + code = StubCache::ComputeCallPreMonomorphic(argc); + break; + case MONOMORPHIC: + if (code->IsUndefined()) code = StubCache::ComputeCallMegamorphic(argc); + break; + default: + // Compute monomorphic stub. + switch (lookup->type()) { + case FIELD: { + int index = lookup->GetFieldIndex(); + code = StubCache::ComputeCallField(argc, *name, *object, + lookup->holder(), index); + break; + } + case CONSTANT_FUNCTION: { + // Get the constant function and compute the code stub for this + // call; used for rewriting to monomorphic state and making sure + // that the code stub is in the stub cache. + JSFunction* function = lookup->GetConstantFunction(); + code = StubCache::ComputeCallConstant(argc, *name, *object, + lookup->holder(), function); + break; + } + case NORMAL: { + // There is only one shared stub for calling normalized + // properties. It does not traverse the prototype chain, so the + // property must be found in the receiver for the stub to be + // applicable. + if (!object->IsJSObject()) return; + Handle receiver = Handle::cast(object); + if (lookup->holder() != *receiver) return; + code = StubCache::ComputeCallNormal(argc, *name, *receiver); + break; + } + case INTERCEPTOR: { + code = StubCache::ComputeCallInterceptor(argc, *name, *object, + lookup->holder()); + break; + } + default: + return; } - default: - return; - } + break; } // If we're unable to compute the stub (not enough memory left), we @@ -447,7 +460,7 @@ void CallIC::UpdateCaches(LookupResult* lookup, } -Object* LoadIC::Load(State state, Handle object, Handle name) { +Object* LoadIC::Load(Handle object, Handle name) { // If the object is undefined or null it's illegal to try to get any // of its properties; throw a TypeError in that case. if (object->IsUndefined() || object->IsNull()) { @@ -520,7 +533,7 @@ Object* LoadIC::Load(State state, Handle object, Handle name) { // Update inline cache and stub cache. if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, object, name); + UpdateCaches(&lookup, object, name); } PropertyAttributes attr; @@ -542,7 +555,6 @@ Object* LoadIC::Load(State state, Handle object, Handle name) { void LoadIC::UpdateCaches(LookupResult* lookup, - State state, Handle object, Handle name) { ASSERT(lookup->IsLoaded()); @@ -555,13 +567,15 @@ void LoadIC::UpdateCaches(LookupResult* lookup, Handle receiver = Handle::cast(object); // Compute the code stub for this load. - Object* code = NULL; + Object* code = Heap::undefined_value(); + State state = ComputeCacheState(target(), *object, *name, &code); + if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay // setting the monomorphic state. code = pre_monomorphic_stub(); - } else { + } else if (state != MONOMORPHIC) { // Compute monomorphic stub. switch (lookup->type()) { case FIELD: { @@ -602,18 +616,20 @@ void LoadIC::UpdateCaches(LookupResult* lookup, default: return; } - } - // If we're unable to compute the stub (not enough memory left), we - // simply avoid updating the caches. - if (code->IsFailure()) return; + // If we're unable to compute the stub (not enough memory left), we + // simply avoid updating the caches. + if (code->IsFailure()) return; + } // Patch the call site depending on the state of the cache. if (state == UNINITIALIZED || state == PREMONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) { set_target(Code::cast(code)); - } else if (state == MONOMORPHIC) { + } else if (state == MONOMORPHIC && code->IsUndefined()) { set_target(megamorphic_stub()); + } else if (state == MONOMORPHIC) { + set_target(Code::cast(code)); } #ifdef DEBUG @@ -622,8 +638,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, } -Object* KeyedLoadIC::Load(State state, - Handle object, +Object* KeyedLoadIC::Load(Handle object, Handle key) { if (key->IsSymbol()) { Handle name = Handle::cast(key); @@ -651,7 +666,7 @@ Object* KeyedLoadIC::Load(State state, if (code->IsFailure()) return code; set_target(Code::cast(code)); #ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); + if (FLAG_trace_ic) PrintF("[KeyedLoadIC : +#length /string]\n"); #endif return Smi::FromInt(string->length()); } @@ -663,7 +678,7 @@ Object* KeyedLoadIC::Load(State state, if (code->IsFailure()) return code; set_target(Code::cast(code)); #ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); + if (FLAG_trace_ic) PrintF("[KeyedLoadIC : +#length /array]\n"); #endif return JSArray::cast(*object)->length(); } @@ -676,7 +691,7 @@ Object* KeyedLoadIC::Load(State state, if (code->IsFailure()) return code; set_target(Code::cast(code)); #ifdef DEBUG - TraceIC("KeyedLoadIC", name, state, target()); + if (FLAG_trace_ic) PrintF("[KeyedLoadIC : +#prototype /function]\n"); #endif return Accessors::FunctionGetPrototype(*object, 0); } @@ -705,7 +720,7 @@ Object* KeyedLoadIC::Load(State state, // Update the inline cache. if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, object, name); + UpdateCaches(&lookup, object, name); } PropertyAttributes attr; @@ -735,8 +750,9 @@ Object* KeyedLoadIC::Load(State state, } -void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, - Handle object, Handle name) { +void KeyedLoadIC::UpdateCaches(LookupResult* lookup, + Handle object, + Handle name) { ASSERT(lookup->IsLoaded()); // Bail out if we didn't find a result. if (!lookup->IsValid() || !lookup->IsCacheable()) return; @@ -744,9 +760,11 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, if (!object->IsJSObject()) return; Handle receiver = Handle::cast(object); - // Compute the code stub for this load. - Object* code = NULL; + // Compute the state of the current inline cache. + Object* code = Heap::undefined_value(); + State state = ComputeCacheState(target(), *object, *name, &code); + // Compute the code stub for this load. if (state == UNINITIALIZED) { // This is the first time we execute this inline cache. // Set the target to the pre monomorphic stub to delay @@ -809,8 +827,7 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, } -Object* StoreIC::Store(State state, - Handle object, +Object* StoreIC::Store(Handle object, Handle name, Handle value) { // If the object is undefined or null it's illegal to try to set any @@ -838,7 +855,7 @@ Object* StoreIC::Store(State state, // Update inline cache and stub cache. if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, receiver, name, value); + UpdateCaches(&lookup, receiver, name, value); } // Set the property. @@ -847,7 +864,6 @@ Object* StoreIC::Store(State state, void StoreIC::UpdateCaches(LookupResult* lookup, - State state, Handle receiver, Handle name, Handle value) { @@ -864,10 +880,13 @@ void StoreIC::UpdateCaches(LookupResult* lookup, // current state. PropertyType type = lookup->type(); + // Compute the state of the current inline cache. + Object* code = Heap::undefined_value(); + State state = ComputeCacheState(target(), *receiver, *name, &code); + // Compute the code stub for this store; used for rewriting to // monomorphic state and making sure that the code stub is in the // stub cache. - Object* code = NULL; switch (type) { case FIELD: { code = StubCache::ComputeStoreField(*name, *receiver, @@ -917,8 +936,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, } -Object* KeyedStoreIC::Store(State state, - Handle object, +Object* KeyedStoreIC::Store(Handle object, Handle key, Handle value) { if (key->IsSymbol()) { @@ -949,7 +967,7 @@ Object* KeyedStoreIC::Store(State state, // Update inline cache and stub cache. if (FLAG_use_ic && lookup.IsLoaded()) { - UpdateCaches(&lookup, state, receiver, name, value); + UpdateCaches(&lookup, receiver, name, value); } // Set the property. @@ -968,7 +986,6 @@ Object* KeyedStoreIC::Store(State state, void KeyedStoreIC::UpdateCaches(LookupResult* lookup, - State state, Handle receiver, Handle name, Handle value) { @@ -988,7 +1005,8 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, // Compute the code stub for this store; used for rewriting to // monomorphic state and making sure that the code stub is in the // stub cache. - Object* code = NULL; + Object* code = Heap::undefined_value(); + State state = ComputeCacheState(target(), *receiver, *name, &code); switch (type) { case FIELD: { @@ -1046,8 +1064,7 @@ Object* CallIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 2); CallIC ic; - IC::State state = IC::StateFrom(ic.target(), args[0]); - return ic.LoadFunction(state, args.at(0), args.at(1)); + return ic.LoadFunction(args.at(0), args.at(1)); } @@ -1071,8 +1088,7 @@ Object* LoadIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 2); LoadIC ic; - IC::State state = IC::StateFrom(ic.target(), args[0]); - return ic.Load(state, args.at(0), args.at(1)); + return ic.Load(args.at(0), args.at(1)); } @@ -1091,8 +1107,7 @@ Object* KeyedLoadIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 2); KeyedLoadIC ic; - IC::State state = IC::StateFrom(ic.target(), args[0]); - return ic.Load(state, args.at(0), args.at(1)); + return ic.Load(args.at(0), args.at(1)); } @@ -1111,9 +1126,7 @@ Object* StoreIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 3); StoreIC ic; - IC::State state = IC::StateFrom(ic.target(), args[0]); - return ic.Store(state, args.at(0), args.at(1), - args.at(2)); + return ic.Store(args.at(0), args.at(1), args.at(2)); } @@ -1132,9 +1145,7 @@ Object* KeyedStoreIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 3); KeyedStoreIC ic; - IC::State state = IC::StateFrom(ic.target(), args[0]); - return ic.Store(state, args.at(0), args.at(1), - args.at(2)); + return ic.Store(args.at(0), args.at(1), args.at(2)); } diff --git a/src/ic.h b/src/ic.h index fa45c7b..b9b9dea 100644 --- a/src/ic.h +++ b/src/ic.h @@ -81,8 +81,12 @@ class IC { Code* target() { return GetTargetAtAddress(address()); } inline Address address(); - // Compute the current IC state based on the target stub and the receiver. - static State StateFrom(Code* target, Object* receiver); + // Compute the state of the current inline cache. If the current + // inline cache is monomorphic, this might change the code cache in + // the receiver map and it might return a code object from the code + // cache in the receiver map that should be used as the new target. + static State ComputeCacheState(Code* target, Object* receiver, + Object* name, Object** new_target); // Clear the inline cache to initial state. static void Clear(Address address); @@ -161,7 +165,7 @@ class CallIC: public IC { public: CallIC() : IC(EXTRA_CALL_FRAME) { ASSERT(target()->is_call_stub()); } - Object* LoadFunction(State state, Handle object, Handle name); + Object* LoadFunction(Handle object, Handle name); // Code generator routines. @@ -179,7 +183,6 @@ class CallIC: public IC { // Update the inline cache and the global stub cache based on the // lookup result. void UpdateCaches(LookupResult* lookup, - State state, Handle object, Handle name); @@ -197,7 +200,7 @@ class LoadIC: public IC { public: LoadIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_load_stub()); } - Object* Load(State state, Handle object, Handle name); + Object* Load(Handle object, Handle name); // Code generator routines. static void GenerateInitialize(MacroAssembler* masm); @@ -219,7 +222,6 @@ class LoadIC: public IC { // Update the inline cache and the global stub cache based on the // lookup result. void UpdateCaches(LookupResult* lookup, - State state, Handle object, Handle name); @@ -243,7 +245,7 @@ class KeyedLoadIC: public IC { public: KeyedLoadIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_keyed_load_stub()); } - Object* Load(State state, Handle object, Handle key); + Object* Load(Handle object, Handle key); // Code generator routines. static void GenerateMiss(MacroAssembler* masm); @@ -256,7 +258,6 @@ class KeyedLoadIC: public IC { // Update the inline cache. void UpdateCaches(LookupResult* lookup, - State state, Handle object, Handle name); @@ -283,8 +284,7 @@ class StoreIC: public IC { public: StoreIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_store_stub()); } - Object* Store(State state, - Handle object, + Object* Store(Handle object, Handle name, Handle value); @@ -299,7 +299,7 @@ class StoreIC: public IC { // Update the inline cache and the global stub cache based on the // lookup result. void UpdateCaches(LookupResult* lookup, - State state, Handle receiver, + Handle receiver, Handle name, Handle value); @@ -320,8 +320,7 @@ class KeyedStoreIC: public IC { public: KeyedStoreIC() : IC(NO_EXTRA_FRAME) { } - Object* Store(State state, - Handle object, + Object* Store(Handle object, Handle name, Handle value); @@ -335,7 +334,6 @@ class KeyedStoreIC: public IC { // Update the inline cache. void UpdateCaches(LookupResult* lookup, - State state, Handle receiver, Handle name, Handle value); diff --git a/src/objects-inl.h b/src/objects-inl.h index 5bcf0a7..deaad57 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2109,6 +2109,20 @@ void Map::ClearCodeCache() { } +Object* Map::FindInCodeCache(String* name, Code::Flags flags) { + int index; + return FindIndexInCodeCache(name, flags, &index); +} + + +void Map::RemoveFromCodeCache(int index) { + FixedArray* array = code_cache(); + ASSERT(array->length() >= index && array->get(index)->IsCode()); + array->set_undefined(index - 1); // key + array->set_undefined(index); // code +} + + #undef CAST_ACCESSOR #undef INT_ACCESSORS #undef SMI_ACCESSORS diff --git a/src/objects.cc b/src/objects.cc index 471864a..ae1b9bb 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -2410,41 +2410,27 @@ Object* Map::UpdateCodeCache(String* name, Code* code) { } -Object* Map::FindInCodeCache(String* name, Code::Flags flags) { +Object* Map::FindIndexInCodeCache(String* name, Code::Flags flags, int* index) { FixedArray* cache = code_cache(); int length = cache->length(); for (int i = 0; i < length; i += 2) { Object* key = cache->get(i); if (key->IsUndefined()) { - return key; + continue; } if (name->Equals(String::cast(key))) { Code* code = Code::cast(cache->get(i + 1)); - if (code->flags() == flags) return code; + if (code->flags() == flags) { + *index = i + 1; + return code; + } } } + *index = -1; return Heap::undefined_value(); } -int Map::IndexInCodeCache(Code* code) { - FixedArray* array = code_cache(); - int len = array->length(); - for (int i = 0; i < len; i += 2) { - if (array->get(i + 1) == code) return i + 1; - } - return -1; -} - - -void Map::RemoveFromCodeCache(int index) { - FixedArray* array = code_cache(); - ASSERT(array->length() >= index && array->get(index)->IsCode()); - array->set_undefined(index - 1); // key - array->set_undefined(index); // code -} - - void FixedArray::FixedArrayIterateBody(ObjectVisitor* v) { IteratePointers(v, kHeaderSize, kHeaderSize + length() * kPointerSize); } diff --git a/src/objects.h b/src/objects.h index 0957a9f..792d8a9 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2342,14 +2342,14 @@ class Map: public HeapObject { Object* UpdateCodeCache(String* name, Code* code); // Returns the found code or undefined if absent. - Object* FindInCodeCache(String* name, Code::Flags flags); + inline Object* FindInCodeCache(String* name, Code::Flags flags); - // Returns the non-negative index of the code object if it is in the - // cache and -1 otherwise. - int IndexInCodeCache(Code* code); + // Returns the found code or undefined if absent. Also returns the + // index at which the code was found or -1 if absent. + Object* FindIndexInCodeCache(String* name, Code::Flags flags, int* index); // Removes a code object from the code cache at the given index. - void RemoveFromCodeCache(int index); + inline void RemoveFromCodeCache(int index); // Dispatched behavior. void MapIterateBody(ObjectVisitor* v); -- 2.7.4