Chromium Code Reviews| Index: src/ic.cc |
| diff --git a/src/ic.cc b/src/ic.cc |
| index 645c6fdcf6542304d6b67901e1c649ede62a566e..431cd866984873ac27a5698369a3d66c5e75195e 100644 |
| --- a/src/ic.cc |
| +++ b/src/ic.cc |
| @@ -154,24 +154,20 @@ static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup, |
| } |
| -IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { |
| - IC::State state = target->ic_state(); |
| - |
| - if (state != MONOMORPHIC || !name->IsString()) return state; |
| - if (receiver->IsUndefined() || receiver->IsNull()) return state; |
| - |
| +static bool TryRemoveInvalidPrototypeDependentStub(Code* target, |
| + Object* receiver, |
| + Object* name) { |
| InlineCacheHolderFlag cache_holder = |
| Code::ExtractCacheHolderFromFlags(target->flags()); |
| - |
| if (cache_holder == OWN_MAP && !receiver->IsJSObject()) { |
| // The stub was generated for JSObject but called for non-JSObject. |
| // IC::GetCodeCacheHolder is not applicable. |
| - return MONOMORPHIC; |
| + return false; |
| } else if (cache_holder == PROTOTYPE_MAP && |
| receiver->GetPrototype()->IsNull()) { |
| // IC::GetCodeCacheHolder is not applicable. |
| - return MONOMORPHIC; |
| + return false; |
| } |
| Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map(); |
| @@ -185,20 +181,37 @@ IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { |
| // to prototype check failure. |
| int index = map->IndexInCodeCache(name, target); |
| if (index >= 0) { |
| - // For keyed load/store/call, 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. |
| - Code::Kind kind = target->kind(); |
| - if (kind == Code::KEYED_LOAD_IC || |
| - kind == Code::KEYED_STORE_IC || |
| - kind == Code::KEYED_CALL_IC) { |
| - return MONOMORPHIC; |
| - } |
| - |
| - // Remove the target from the code cache to avoid hitting the same |
| - // invalid stub again. |
| map->RemoveFromCodeCache(String::cast(name), target, index); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| + |
| +IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { |
| + IC::State state = target->ic_state(); |
| + |
| + if (state != MONOMORPHIC || !name->IsString()) return state; |
| + if (receiver->IsUndefined() || receiver->IsNull()) return state; |
| + |
| + // For keyed load/store/call, 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. |
| + Code::Kind kind = target->kind(); |
| + if (kind == Code::KEYED_LOAD_IC || |
| + kind == Code::KEYED_STORE_IC || |
| + kind == Code::KEYED_CALL_IC) { |
| + return MONOMORPHIC; |
| + } |
| + |
| + // Remove the target from the code cache if it became invalid |
| + // because of changes in the prototype chain to avoid hitting it |
| + // again. |
| + // Call stubs handle this later to allow extra IC state |
| + // transitions. |
| + if (kind != Code::CALL_IC && |
| + TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) { |
| return MONOMORPHIC_PROTOTYPE_FAILURE; |
| } |
| @@ -482,6 +495,7 @@ void CallICBase::ReceiverToObject(Handle<Object> object) { |
| MaybeObject* CallICBase::LoadFunction(State state, |
| + Code::ExtraICState extra_ic_state, |
| Handle<Object> object, |
| Handle<String> name) { |
| // If the object is undefined or null it's illegal to try to get any |
| @@ -527,7 +541,7 @@ MaybeObject* CallICBase::LoadFunction(State state, |
| // Lookup is valid: Update inline cache and stub cache. |
| if (FLAG_use_ic) { |
| - UpdateCaches(&lookup, state, object, name); |
| + UpdateCaches(&lookup, state, extra_ic_state, object, name); |
| } |
| // Get the property. |
| @@ -576,8 +590,57 @@ MaybeObject* CallICBase::LoadFunction(State state, |
| } |
| +bool CallICBase::TryUpdateExtraICState(LookupResult* lookup, |
| + Handle<Object> object, |
| + Code::ExtraICState* extra_ic_state) { |
| + ASSERT(kind_ == Code::CALL_IC); |
| + if (lookup->type() != CONSTANT_FUNCTION) return false; |
| + JSFunction* function = lookup->GetConstantFunction(); |
| + if (!function->shared()->HasBuiltinFunctionId()) return false; |
| + |
| + // Fetch the arguments passed to the called function. |
| + const int argc = target()->arguments_count(); |
| + Address entry = Top::c_entry_fp(Top::GetCurrentThread()); |
| + Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset); |
| + Arguments args(argc + 1, |
| + &Memory::Object_at(fp + |
| + StandardFrameConstants::kCallerSPOffset + |
| + argc * kPointerSize)); |
| + switch (function->shared()->builtin_function_id()) { |
| + case kStringCharCodeAt: |
| + case kStringCharAt: |
| + if (object->IsString()) { |
| + String* string = String::cast(*object); |
| + // Check that there's the right wrapper in the receiver slot. |
| + ASSERT(string == JSValue::cast(args[0])->value()); |
| + // If we're in the default (fastest) state and the index is |
| + // out of bounds, update the state to record this fact. |
| + if (*extra_ic_state == DEFAULT_STRING_STUB && |
| + argc >= 1 && args[1]->IsNumber()) { |
| + double index; |
| + if (args[1]->IsSmi()) { |
| + index = Smi::cast(args[1])->value(); |
| + } else { |
| + ASSERT(args[1]->IsHeapNumber()); |
| + index = DoubleToInteger(HeapNumber::cast(args[1])->value()); |
| + } |
| + if (index < 0 || index >= string->length()) { |
| + *extra_ic_state = STRING_INDEX_OUT_OF_BOUNDS; |
| + return true; |
| + } |
| + } |
| + } |
| + break; |
| + default: |
| + return false; |
| + } |
| + return false; |
| +} |
| + |
| + |
| void CallICBase::UpdateCaches(LookupResult* lookup, |
| State state, |
| + Code::ExtraICState extra_ic_state, |
| Handle<Object> object, |
| Handle<String> name) { |
| // Bail out if we didn't find a result. |
| @@ -590,6 +653,17 @@ void CallICBase::UpdateCaches(LookupResult* lookup, |
| return; |
| } |
| + |
| + if (kind_ == Code::CALL_IC && state == MONOMORPHIC) { |
|
Mads Ager (chromium)
2011/01/18 12:54:00
This is non-trivial and requires a comment.
Woul
|
| + if (TryUpdateExtraICState(lookup, object, &extra_ic_state)) { |
| + state = PREMONOMORPHIC; |
| + } else if (TryRemoveInvalidPrototypeDependentStub(target(), |
| + *object, |
| + *name)) { |
| + state = MONOMORPHIC_PROTOTYPE_FAILURE; |
| + } |
| + } |
| + |
| // Compute the number of arguments. |
| int argc = target()->arguments_count(); |
| InLoopFlag in_loop = target()->ic_in_loop(); |
| @@ -624,6 +698,7 @@ void CallICBase::UpdateCaches(LookupResult* lookup, |
| maybe_code = StubCache::ComputeCallConstant(argc, |
| in_loop, |
| kind_, |
| + extra_ic_state, |
| *name, |
| *object, |
| lookup->holder(), |
| @@ -707,7 +782,10 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, |
| Handle<Object> object, |
| Handle<Object> key) { |
| if (key->IsSymbol()) { |
| - return CallICBase::LoadFunction(state, object, Handle<String>::cast(key)); |
| + return CallICBase::LoadFunction(state, |
| + Code::kNoExtraICState, |
| + object, |
| + Handle<String>::cast(key)); |
| } |
| if (object->IsUndefined() || object->IsNull()) { |
| @@ -1641,11 +1719,13 @@ MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) { |
| ASSERT(args.length() == 2); |
| CallIC ic; |
| IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); |
| + Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state(); |
| + MaybeObject* maybe_result = ic.LoadFunction(state, |
| + extra_ic_state, |
| + args.at<Object>(0), |
| + args.at<String>(1)); |
| Object* result; |
| - { MaybeObject* maybe_result = |
| - ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1)); |
| - if (!maybe_result->ToObject(&result)) return maybe_result; |
| - } |
| + if (!maybe_result->ToObject(&result)) return maybe_result; |
| // The first time the inline cache is updated may be the first time the |
| // function it references gets called. If the function was lazily compiled |