From 11a4cb5718b5dc7f75ae7fa1254fd793ab8dbebd Mon Sep 17 00:00:00 2001 From: "vitalyr@chromium.org" Date: Tue, 18 Jan 2011 16:54:48 +0000 Subject: [PATCH] Introduce extra IC state to record additional feedback from IC-s. Extra IC state is only two bits and only supported for call IC-s for now. To change its extra state an IC stub jumps to a new miss stub that goes to runtime as usual but then instead of going megamorphic generates a monomorphic stub with the updated state. Review URL: http://codereview.chromium.org/6344005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6370 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/ic-arm.cc | 8 +- src/arm/stub-cache-arm.cc | 54 +++++--- src/ia32/ic-ia32.cc | 12 +- src/ia32/stub-cache-ia32.cc | 49 +++++-- src/ic.cc | 309 ++++++++++++++++++++++++++++++-------------- src/ic.h | 13 ++ src/objects-inl.h | 23 +++- src/objects.h | 27 ++-- src/stub-cache.cc | 130 ++++++++++++------- src/stub-cache.h | 18 ++- src/type-info.cc | 15 ++- src/type-info.h | 6 + src/x64/ic-x64.cc | 8 +- src/x64/stub-cache-x64.cc | 46 +++++-- 14 files changed, 505 insertions(+), 213 deletions(-) diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 340bc1e..6120bba 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -542,8 +542,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, Label number, non_number, non_string, boolean, probe, miss; // Probe the stub cache. - Code::Flags flags = - Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + MONOMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5); // If the stub cache probing failed, the receiver might be a value. diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 20e2801..b7ec5d2 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -1332,11 +1332,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, MaybeObject* CallStubCompiler::GenerateMissBranch() { + MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(), + kind_); Object* obj; - { MaybeObject* maybe_obj = - StubCache::ComputeCallMiss(arguments().immediate(), kind_); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + if (!maybe_obj->ToObject(&obj)) return maybe_obj; __ Jump(Handle(Code::cast(obj)), RelocInfo::CODE_TARGET); return obj; } @@ -1646,8 +1645,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; - GenerateNameCheck(name, &miss); + Label* index_out_of_range_label = &index_out_of_range; + + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1675,7 +1681,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_code_at_generator.GenerateFast(masm()); __ Drop(argc + 1); @@ -1684,12 +1690,17 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( StubRuntimeCallHelper call_helper; char_code_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ LoadRoot(r0, Heap::kNanValueRootIndex); - __ Drop(argc + 1); - __ Ret(); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ LoadRoot(r0, Heap::kNanValueRootIndex); + __ Drop(argc + 1); + __ Ret(); + } __ bind(&miss); + // Restore function name in r2. + __ Move(r2, Handle(name)); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -1720,9 +1731,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; + Label* index_out_of_range_label = &index_out_of_range; - GenerateNameCheck(name, &miss); + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1752,7 +1769,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_at_generator.GenerateFast(masm()); __ Drop(argc + 1); @@ -1761,12 +1778,17 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( StubRuntimeCallHelper call_helper; char_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ LoadRoot(r0, Heap::kEmptyStringRootIndex); - __ Drop(argc + 1); - __ Ret(); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ LoadRoot(r0, Heap::kEmptyStringRootIndex); + __ Drop(argc + 1); + __ Ret(); + } __ bind(&miss); + // Restore function name in r2. + __ Move(r2, Handle(name)); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 90bfd4b..f570fe0 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -1231,8 +1231,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, Label number, non_number, non_string, boolean, probe, miss; // Probe the stub cache. - Code::Flags flags = - Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + MONOMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, eax); // If the stub cache probing failed, the receiver might be a value. @@ -1325,7 +1329,9 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) { } -static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) { +static void GenerateCallMiss(MacroAssembler* masm, + int argc, + IC::UtilityId id) { // ----------- S t a t e ------------- // -- ecx : name // -- esp[0] : return address diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index bcb02ed..45d63c5 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1362,11 +1362,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, MaybeObject* CallStubCompiler::GenerateMissBranch() { + MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(), + kind_); Object* obj; - { MaybeObject* maybe_obj = - StubCache::ComputeCallMiss(arguments().immediate(), kind_); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + if (!maybe_obj->ToObject(&obj)) return maybe_obj; __ jmp(Handle(Code::cast(obj)), RelocInfo::CODE_TARGET); return obj; } @@ -1685,9 +1684,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; + Label* index_out_of_range_label = &index_out_of_range; - GenerateNameCheck(name, &miss); + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1715,7 +1720,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_code_at_generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); @@ -1723,11 +1728,16 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( StubRuntimeCallHelper call_helper; char_code_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ Set(eax, Immediate(Factory::nan_value())); - __ ret((argc + 1) * kPointerSize); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ Set(eax, Immediate(Factory::nan_value())); + __ ret((argc + 1) * kPointerSize); + } __ bind(&miss); + // Restore function name in ecx. + __ Set(ecx, Immediate(Handle(name))); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -1758,9 +1768,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; + Label* index_out_of_range_label = &index_out_of_range; - GenerateNameCheck(name, &miss); + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1790,7 +1806,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_at_generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); @@ -1798,11 +1814,16 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( StubRuntimeCallHelper call_helper; char_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ Set(eax, Immediate(Factory::empty_string())); - __ ret((argc + 1) * kPointerSize); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ Set(eax, Immediate(Factory::empty_string())); + __ ret((argc + 1) * kPointerSize); + } __ bind(&miss); + // Restore function name in ecx. + __ Set(ecx, Immediate(Handle(name))); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; diff --git a/src/ic.cc b/src/ic.cc index 645c6fd..afae323 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) { MaybeObject* CallICBase::LoadFunction(State state, + Code::ExtraICState extra_ic_state, Handle object, Handle 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,142 @@ MaybeObject* CallICBase::LoadFunction(State state, } +bool CallICBase::TryUpdateExtraICState(LookupResult* lookup, + Handle 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; +} + + +MaybeObject* CallICBase::ComputeMonomorphicStub( + LookupResult* lookup, + State state, + Code::ExtraICState extra_ic_state, + Handle object, + Handle name) { + int argc = target()->arguments_count(); + InLoopFlag in_loop = target()->ic_in_loop(); + MaybeObject* maybe_code = NULL; + switch (lookup->type()) { + case FIELD: { + int index = lookup->GetFieldIndex(); + maybe_code = StubCache::ComputeCallField(argc, + in_loop, + kind_, + *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(); + maybe_code = StubCache::ComputeCallConstant(argc, + in_loop, + kind_, + extra_ic_state, + *name, + *object, + lookup->holder(), + function); + break; + } + case NORMAL: { + if (!object->IsJSObject()) return NULL; + Handle receiver = Handle::cast(object); + + if (lookup->holder()->IsGlobalObject()) { + GlobalObject* global = GlobalObject::cast(lookup->holder()); + JSGlobalPropertyCell* cell = + JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); + if (!cell->value()->IsJSFunction()) return NULL; + JSFunction* function = JSFunction::cast(cell->value()); + maybe_code = StubCache::ComputeCallGlobal(argc, + in_loop, + kind_, + *name, + *receiver, + global, + cell, + function); + } else { + // 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 (lookup->holder() != *receiver) return NULL; + maybe_code = StubCache::ComputeCallNormal(argc, + in_loop, + kind_, + *name, + *receiver); + } + break; + } + case INTERCEPTOR: { + ASSERT(HasInterceptorGetter(lookup->holder())); + maybe_code = StubCache::ComputeCallInterceptor(argc, + kind_, + *name, + *object, + lookup->holder()); + break; + } + default: + maybe_code = NULL; + break; + } + return maybe_code; +} + + void CallICBase::UpdateCaches(LookupResult* lookup, State state, + Code::ExtraICState extra_ic_state, Handle object, Handle name) { // Bail out if we didn't find a result. @@ -594,90 +742,44 @@ void CallICBase::UpdateCaches(LookupResult* lookup, int argc = target()->arguments_count(); InLoopFlag in_loop = target()->ic_in_loop(); MaybeObject* maybe_code = NULL; - Object* code; + bool had_proto_failure = false; 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. maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_); } else if (state == MONOMORPHIC) { - maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_); - } else { - // Compute monomorphic stub. - switch (lookup->type()) { - case FIELD: { - int index = lookup->GetFieldIndex(); - maybe_code = StubCache::ComputeCallField(argc, - in_loop, - kind_, - *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(); - maybe_code = StubCache::ComputeCallConstant(argc, - in_loop, - kind_, - *name, - *object, - lookup->holder(), - function); - break; - } - case NORMAL: { - if (!object->IsJSObject()) return; - Handle receiver = Handle::cast(object); - - if (lookup->holder()->IsGlobalObject()) { - GlobalObject* global = GlobalObject::cast(lookup->holder()); - JSGlobalPropertyCell* cell = - JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); - if (!cell->value()->IsJSFunction()) return; - JSFunction* function = JSFunction::cast(cell->value()); - maybe_code = StubCache::ComputeCallGlobal(argc, - in_loop, - kind_, - *name, - *receiver, - global, - cell, - function); - } else { - // 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 (lookup->holder() != *receiver) return; - maybe_code = StubCache::ComputeCallNormal(argc, - in_loop, - kind_, - *name, - *receiver); - } - break; - } - case INTERCEPTOR: { - ASSERT(HasInterceptorGetter(lookup->holder())); - maybe_code = StubCache::ComputeCallInterceptor(argc, - kind_, - *name, - *object, - lookup->holder()); - break; - } - default: - return; + if (kind_ == Code::CALL_IC && + TryUpdateExtraICState(lookup, object, &extra_ic_state)) { + maybe_code = ComputeMonomorphicStub(lookup, + state, + extra_ic_state, + object, + name); + } else if (kind_ == Code::CALL_IC && + TryRemoveInvalidPrototypeDependentStub(target(), + *object, + *name)) { + had_proto_failure = true; + maybe_code = ComputeMonomorphicStub(lookup, + state, + extra_ic_state, + object, + name); + } else { + maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_); } + } else { + maybe_code = ComputeMonomorphicStub(lookup, + state, + extra_ic_state, + object, + name); } // If we're unable to compute the stub (not enough memory left), we // simply avoid updating the caches. + Object* code; if (maybe_code == NULL || !maybe_code->ToObject(&code)) return; // Patch the call site depending on the state of the cache. @@ -696,7 +798,9 @@ void CallICBase::UpdateCaches(LookupResult* lookup, StubCache::Set(*name, map, Code::cast(code)); } + USE(had_proto_failure); #ifdef DEBUG + if (had_proto_failure) state = MONOMORPHIC_PROTOTYPE_FAILURE; TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC", name, state, target(), in_loop ? " (in-loop)" : ""); #endif @@ -707,7 +811,10 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, Handle object, Handle key) { if (key->IsSymbol()) { - return CallICBase::LoadFunction(state, object, Handle::cast(key)); + return CallICBase::LoadFunction(state, + Code::kNoExtraICState, + object, + Handle::cast(key)); } if (object->IsUndefined() || object->IsNull()) { @@ -1641,11 +1748,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(0), + args.at(1)); Object* result; - { MaybeObject* maybe_result = - ic.LoadFunction(state, args.at(0), args.at(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 diff --git a/src/ic.h b/src/ic.h index 8562bcc..9996aff 100644 --- a/src/ic.h +++ b/src/ic.h @@ -193,16 +193,29 @@ class CallICBase: public IC { public: MUST_USE_RESULT MaybeObject* LoadFunction(State state, + Code::ExtraICState extra_ic_state, Handle object, Handle name); protected: Code::Kind kind_; + bool TryUpdateExtraICState(LookupResult* lookup, + Handle object, + Code::ExtraICState* extra_ic_state); + + MUST_USE_RESULT MaybeObject* ComputeMonomorphicStub( + LookupResult* lookup, + State state, + Code::ExtraICState extra_ic_state, + Handle object, + Handle name); + // Update the inline cache and the global stub cache based on the // lookup result. void UpdateCaches(LookupResult* lookup, State state, + Code::ExtraICState extra_ic_state, Handle object, Handle name); diff --git a/src/objects-inl.h b/src/objects-inl.h index de0ffa3..df44674 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2416,6 +2416,12 @@ InlineCacheState Code::ic_state() { } +Code::ExtraICState Code::extra_ic_state() { + ASSERT(is_inline_cache_stub()); + return ExtractExtraICStateFromFlags(flags()); +} + + PropertyType Code::type() { ASSERT(ic_state() == MONOMORPHIC); return ExtractTypeFromFlags(flags()); @@ -2592,14 +2598,20 @@ bool Code::is_inline_cache_stub() { Code::Flags Code::ComputeFlags(Kind kind, InLoopFlag in_loop, InlineCacheState ic_state, + ExtraICState extra_ic_state, PropertyType type, int argc, InlineCacheHolderFlag holder) { + // Extra IC state is only allowed for monomorphic call IC stubs. + ASSERT(extra_ic_state == kNoExtraICState || + (kind == CALL_IC && (ic_state == MONOMORPHIC || + ic_state == MONOMORPHIC_PROTOTYPE_FAILURE))); // Compute the bit mask. int bits = kind << kFlagsKindShift; if (in_loop) bits |= kFlagsICInLoopMask; bits |= ic_state << kFlagsICStateShift; bits |= type << kFlagsTypeShift; + bits |= extra_ic_state << kFlagsExtraICStateShift; bits |= argc << kFlagsArgumentsCountShift; if (holder == PROTOTYPE_MAP) bits |= kFlagsCacheInPrototypeMapMask; // Cast to flags and validate result before returning it. @@ -2608,6 +2620,7 @@ Code::Flags Code::ComputeFlags(Kind kind, ASSERT(ExtractICStateFromFlags(result) == ic_state); ASSERT(ExtractICInLoopFromFlags(result) == in_loop); ASSERT(ExtractTypeFromFlags(result) == type); + ASSERT(ExtractExtraICStateFromFlags(result) == extra_ic_state); ASSERT(ExtractArgumentsCountFromFlags(result) == argc); return result; } @@ -2615,10 +2628,12 @@ Code::Flags Code::ComputeFlags(Kind kind, Code::Flags Code::ComputeMonomorphicFlags(Kind kind, PropertyType type, + ExtraICState extra_ic_state, InlineCacheHolderFlag holder, InLoopFlag in_loop, int argc) { - return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc, holder); + return ComputeFlags( + kind, in_loop, MONOMORPHIC, extra_ic_state, type, argc, holder); } @@ -2634,6 +2649,12 @@ InlineCacheState Code::ExtractICStateFromFlags(Flags flags) { } +Code::ExtraICState Code::ExtractExtraICStateFromFlags(Flags flags) { + int bits = (flags & kFlagsExtraICStateMask) >> kFlagsExtraICStateShift; + return static_cast(bits); +} + + InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) { int bits = (flags & kFlagsICInLoopMask); return bits != 0 ? IN_LOOP : NOT_IN_LOOP; diff --git a/src/objects.h b/src/objects.h index 9a577f1..c919513 100644 --- a/src/objects.h +++ b/src/objects.h @@ -3168,6 +3168,10 @@ class Code: public HeapObject { NUMBER_OF_KINDS = LAST_IC_KIND + 1 }; + typedef int ExtraICState; + + static const ExtraICState kNoExtraICState = 0; + #ifdef ENABLE_DISASSEMBLER // Printing static const char* Kind2String(Kind kind); @@ -3203,6 +3207,7 @@ class Code: public HeapObject { // [flags]: Access to specific code flags. inline Kind kind(); inline InlineCacheState ic_state(); // Only valid for IC stubs. + inline ExtraICState extra_ic_state(); // Only valid for IC stubs. inline InLoopFlag ic_in_loop(); // Only valid for IC stubs. inline PropertyType type(); // Only valid for monomorphic IC stubs. inline int arguments_count(); // Only valid for call IC stubs. @@ -3287,22 +3292,26 @@ class Code: public HeapObject { Map* FindFirstMap(); // Flags operations. - static inline Flags ComputeFlags(Kind kind, - InLoopFlag in_loop = NOT_IN_LOOP, - InlineCacheState ic_state = UNINITIALIZED, - PropertyType type = NORMAL, - int argc = -1, - InlineCacheHolderFlag holder = OWN_MAP); + static inline Flags ComputeFlags( + Kind kind, + InLoopFlag in_loop = NOT_IN_LOOP, + InlineCacheState ic_state = UNINITIALIZED, + ExtraICState extra_ic_state = kNoExtraICState, + PropertyType type = NORMAL, + int argc = -1, + InlineCacheHolderFlag holder = OWN_MAP); static inline Flags ComputeMonomorphicFlags( Kind kind, PropertyType type, + ExtraICState extra_ic_state = kNoExtraICState, InlineCacheHolderFlag holder = OWN_MAP, InLoopFlag in_loop = NOT_IN_LOOP, int argc = -1); static inline Kind ExtractKindFromFlags(Flags flags); static inline InlineCacheState ExtractICStateFromFlags(Flags flags); + static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags); static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags); static inline PropertyType ExtractTypeFromFlags(Flags flags); static inline int ExtractArgumentsCountFromFlags(Flags flags); @@ -3423,14 +3432,16 @@ class Code: public HeapObject { static const int kFlagsTypeShift = 4; static const int kFlagsKindShift = 7; static const int kFlagsICHolderShift = 11; - static const int kFlagsArgumentsCountShift = 12; + static const int kFlagsExtraICStateShift = 12; + static const int kFlagsArgumentsCountShift = 14; static const int kFlagsICStateMask = 0x00000007; // 00000000111 static const int kFlagsICInLoopMask = 0x00000008; // 00000001000 static const int kFlagsTypeMask = 0x00000070; // 00001110000 static const int kFlagsKindMask = 0x00000780; // 11110000000 static const int kFlagsCacheInPrototypeMapMask = 0x00000800; - static const int kFlagsArgumentsCountMask = 0xFFFFF000; + static const int kFlagsExtraICStateMask = 0x00003000; + static const int kFlagsArgumentsCountMask = 0xFFFFC000; static const int kFlagsNotUsedInLookup = (kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask); diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 47716d8..295cc4a 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -613,6 +613,7 @@ MaybeObject* StubCache::ComputeKeyedStoreField(String* name, MaybeObject* StubCache::ComputeCallConstant(int argc, InLoopFlag in_loop, Code::Kind kind, + Code::ExtraICState extra_ic_state, String* name, Object* object, JSObject* holder, @@ -632,12 +633,12 @@ MaybeObject* StubCache::ComputeCallConstant(int argc, check = BOOLEAN_CHECK; } - Code::Flags flags = - Code::ComputeMonomorphicFlags(kind, - CONSTANT_FUNCTION, - cache_holder, - in_loop, - argc); + Code::Flags flags = Code::ComputeMonomorphicFlags(kind, + CONSTANT_FUNCTION, + extra_ic_state, + cache_holder, + in_loop, + argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { // If the function hasn't been compiled yet, we cannot do it now @@ -646,7 +647,8 @@ MaybeObject* StubCache::ComputeCallConstant(int argc, // caches. if (!function->is_compiled()) return Failure::InternalError(); // Compile the stub - only create stubs for fully compiled functions. - CallStubCompiler compiler(argc, in_loop, kind, cache_holder); + CallStubCompiler compiler( + argc, in_loop, kind, extra_ic_state, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallConstant(object, holder, function, name, check); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -687,12 +689,14 @@ MaybeObject* StubCache::ComputeCallField(int argc, Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, + Code::kNoExtraICState, cache_holder, in_loop, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - CallStubCompiler compiler(argc, in_loop, kind, cache_holder); + CallStubCompiler compiler( + argc, in_loop, kind, Code::kNoExtraICState, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallField(JSObject::cast(object), holder, @@ -731,15 +735,16 @@ MaybeObject* StubCache::ComputeCallInterceptor(int argc, object = holder; } - Code::Flags flags = - Code::ComputeMonomorphicFlags(kind, - INTERCEPTOR, - cache_holder, - NOT_IN_LOOP, - argc); + Code::Flags flags = Code::ComputeMonomorphicFlags(kind, + INTERCEPTOR, + Code::kNoExtraICState, + cache_holder, + NOT_IN_LOOP, + argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - CallStubCompiler compiler(argc, NOT_IN_LOOP, kind, cache_holder); + CallStubCompiler compiler( + argc, NOT_IN_LOOP, kind, Code::kNoExtraICState, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallInterceptor(JSObject::cast(object), holder, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -782,12 +787,12 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(receiver, holder); JSObject* map_holder = IC::GetCodeCacheHolder(receiver, cache_holder); - Code::Flags flags = - Code::ComputeMonomorphicFlags(kind, - NORMAL, - cache_holder, - in_loop, - argc); + Code::Flags flags = Code::ComputeMonomorphicFlags(kind, + NORMAL, + Code::kNoExtraICState, + cache_holder, + in_loop, + argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { // If the function hasn't been compiled yet, we cannot do it now @@ -795,7 +800,8 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, // internal error which will make sure we do not update any // caches. if (!function->is_compiled()) return Failure::InternalError(); - CallStubCompiler compiler(argc, in_loop, kind, cache_holder); + CallStubCompiler compiler( + argc, in_loop, kind, Code::kNoExtraICState, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -862,8 +868,12 @@ static MaybeObject* FillCache(MaybeObject* maybe_code) { Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + in_loop, + UNINITIALIZED, + Code::kNoExtraICState, + NORMAL, + argc); Object* result = ProbeCache(flags)->ToObjectUnchecked(); ASSERT(!result->IsUndefined()); // This might be called during the marking phase of the collector @@ -875,8 +885,12 @@ Code* StubCache::FindCallInitialize(int argc, MaybeObject* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + in_loop, + UNINITIALIZED, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -918,8 +932,12 @@ Handle StubCache::ComputeKeyedCallInitialize(int argc, MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, in_loop, PREMONOMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + in_loop, + PREMONOMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -933,8 +951,12 @@ MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc, MaybeObject* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, in_loop, MONOMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + in_loop, + MONOMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -948,8 +970,12 @@ MaybeObject* StubCache::ComputeCallNormal(int argc, MaybeObject* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, in_loop, MEGAMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + in_loop, + MEGAMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -963,8 +989,13 @@ MaybeObject* StubCache::ComputeCallMegamorphic(int argc, MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. - Code::Flags flags = Code::ComputeFlags( - kind, NOT_IN_LOOP, MONOMORPHIC_PROTOTYPE_FAILURE, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + MONOMORPHIC_PROTOTYPE_FAILURE, + Code::kNoExtraICState, + NORMAL, + argc, + OWN_MAP); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -977,8 +1008,12 @@ MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) { #ifdef ENABLE_DEBUGGER_SUPPORT MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + DEBUG_BREAK, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -991,12 +1026,12 @@ MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { MaybeObject* StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { - Code::Flags flags = - Code::ComputeFlags(kind, - NOT_IN_LOOP, - DEBUG_PREPARE_STEP_IN, - NORMAL, - argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + DEBUG_PREPARE_STEP_IN, + Code::kNoExtraICState, + NORMAL, + argc); Object* probe; { MaybeObject* maybe_probe = ProbeCache(flags); if (!maybe_probe->ToObject(&probe)) return maybe_probe; @@ -1533,11 +1568,13 @@ MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) { CallStubCompiler::CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind, + Code::ExtraICState extra_ic_state, InlineCacheHolderFlag cache_holder) - : arguments_(argc) - , in_loop_(in_loop) - , kind_(kind) - , cache_holder_(cache_holder) { + : arguments_(argc), + in_loop_(in_loop), + kind_(kind), + extra_ic_state_(extra_ic_state), + cache_holder_(cache_holder) { } @@ -1574,6 +1611,7 @@ MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) { int argc = arguments_.immediate(); Code::Flags flags = Code::ComputeMonomorphicFlags(kind_, type, + extra_ic_state_, cache_holder_, in_loop_, argc); diff --git a/src/stub-cache.h b/src/stub-cache.h index a7829a6..85dd5f6 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -177,13 +177,15 @@ class StubCache : public AllStatic { JSObject* holder, int index); - MUST_USE_RESULT static MaybeObject* ComputeCallConstant(int argc, - InLoopFlag in_loop, - Code::Kind, - String* name, - Object* object, - JSObject* holder, - JSFunction* function); + MUST_USE_RESULT static MaybeObject* ComputeCallConstant( + int argc, + InLoopFlag in_loop, + Code::Kind, + Code::ExtraICState extra_ic_state, + String* name, + Object* object, + JSObject* holder, + JSFunction* function); MUST_USE_RESULT static MaybeObject* ComputeCallNormal(int argc, InLoopFlag in_loop, @@ -660,6 +662,7 @@ class CallStubCompiler: public StubCompiler { CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind, + Code::ExtraICState extra_ic_state, InlineCacheHolderFlag cache_holder); MUST_USE_RESULT MaybeObject* CompileCallField(JSObject* object, @@ -705,6 +708,7 @@ class CallStubCompiler: public StubCompiler { const ParameterCount arguments_; const InLoopFlag in_loop_; const Code::Kind kind_; + const Code::ExtraICState extra_ic_state_; const InlineCacheHolderFlag cache_holder_; const ParameterCount& arguments() { return arguments_; } diff --git a/src/type-info.cc b/src/type-info.cc index 032d985..f4f65e9 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -58,6 +58,9 @@ TypeInfo TypeInfo::TypeFromValue(Handle value) { } +STATIC_ASSERT(DEFAULT_STRING_STUB == Code::kNoExtraICState); + + TypeFeedbackOracle::TypeFeedbackOracle(Handle code, Handle global_context) { global_context_ = global_context; @@ -117,8 +120,16 @@ ZoneMapList* TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr, ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr, Handle name) { int arity = expr->arguments()->length(); - Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::CALL_IC, NORMAL, OWN_MAP, NOT_IN_LOOP, arity); + // Note: these flags won't let us get maps from stubs with + // non-default extra ic state in the megamorphic case. In the more + // important monomorphic case the map is obtained directly, so it's + // not a problem until we decide to emit more polymorphic code. + Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, + NORMAL, + Code::kNoExtraICState, + OWN_MAP, + NOT_IN_LOOP, + arity); return CollectReceiverTypes(expr->position(), name, flags); } diff --git a/src/type-info.h b/src/type-info.h index 98d97de..e026e88 100644 --- a/src/type-info.h +++ b/src/type-info.h @@ -219,6 +219,12 @@ class TypeInfo { }; +enum StringStubFeedback { + DEFAULT_STRING_STUB = 0, + STRING_INDEX_OUT_OF_BOUNDS = 1 +}; + + // Forward declarations. class Assignment; class BinaryOperation; diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 29cbed0..b54aeb9 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -1178,8 +1178,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, Label number, non_number, non_string, boolean, probe, miss; // Probe the stub cache. - Code::Flags flags = - Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); + Code::Flags flags = Code::ComputeFlags(kind, + NOT_IN_LOOP, + MONOMORPHIC, + Code::kNoExtraICState, + NORMAL, + argc); StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, rax); // If the stub cache probing failed, the receiver might be a value. diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 9b3719a..8c1b579 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -1327,8 +1327,8 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = - StubCache::ComputeCallMiss(arguments().immediate(), kind_); + MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(), + kind_); Object* obj; if (!maybe_obj->ToObject(&obj)) return maybe_obj; __ Jump(Handle(Code::cast(obj)), RelocInfo::CODE_TARGET); @@ -1660,9 +1660,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; + Label* index_out_of_range_label = &index_out_of_range; - GenerateNameCheck(name, &miss); + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1690,7 +1696,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_code_at_generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); @@ -1698,11 +1704,16 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( StubRuntimeCallHelper call_helper; char_code_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ LoadRoot(rax, Heap::kNanValueRootIndex); - __ ret((argc + 1) * kPointerSize); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ LoadRoot(rax, Heap::kNanValueRootIndex); + __ ret((argc + 1) * kPointerSize); + } __ bind(&miss); + // Restore function name in rcx. + __ Move(rcx, Handle(name)); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; @@ -1733,9 +1744,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( const int argc = arguments().immediate(); Label miss; + Label name_miss; Label index_out_of_range; + Label* index_out_of_range_label = &index_out_of_range; - GenerateNameCheck(name, &miss); + if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) { + index_out_of_range_label = &miss; + } + + GenerateNameCheck(name, &name_miss); // Check that the maps starting from the prototype haven't changed. GenerateDirectLoadGlobalFunctionPrototype(masm(), @@ -1765,7 +1782,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( result, &miss, // When not a string. &miss, // When not a number. - &index_out_of_range, + index_out_of_range_label, STRING_INDEX_IS_NUMBER); char_at_generator.GenerateFast(masm()); __ ret((argc + 1) * kPointerSize); @@ -1773,11 +1790,16 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall( StubRuntimeCallHelper call_helper; char_at_generator.GenerateSlow(masm(), call_helper); - __ bind(&index_out_of_range); - __ LoadRoot(rax, Heap::kEmptyStringRootIndex); - __ ret((argc + 1) * kPointerSize); + if (index_out_of_range.is_linked()) { + __ bind(&index_out_of_range); + __ LoadRoot(rax, Heap::kEmptyStringRootIndex); + __ ret((argc + 1) * kPointerSize); + } __ bind(&miss); + // Restore function name in rcx. + __ Move(rcx, Handle(name)); + __ bind(&name_miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; -- 2.7.4