| Index: src/ic.cc
|
| ===================================================================
|
| --- src/ic.cc (revision 6800)
|
| +++ src/ic.cc (working copy)
|
| @@ -154,24 +154,20 @@
|
| }
|
|
|
|
|
| -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 @@
|
| // 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;
|
| }
|
|
|
| @@ -329,7 +342,10 @@
|
| void StoreIC::Clear(Address address, Code* target) {
|
| if (target->ic_state() == UNINITIALIZED) return;
|
| ClearInlinedVersion(address);
|
| - SetTargetAtAddress(address, initialize_stub());
|
| + SetTargetAtAddress(address,
|
| + target->extra_ic_state() == kStoreICStrict
|
| + ? initialize_stub_strict()
|
| + : initialize_stub());
|
| }
|
|
|
|
|
| @@ -354,55 +370,6 @@
|
| }
|
|
|
|
|
| -Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) {
|
| - switch (elements_kind) {
|
| - case JSObject::EXTERNAL_BYTE_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray);
|
| - case JSObject::EXTERNAL_SHORT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| - return Builtins::builtin(
|
| - Builtins::KeyedLoadIC_ExternalUnsignedShortArray);
|
| - case JSObject::EXTERNAL_INT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray);
|
| - case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray);
|
| - default:
|
| - UNREACHABLE();
|
| - return NULL;
|
| - }
|
| -}
|
| -
|
| -
|
| -Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) {
|
| - switch (elements_kind) {
|
| - case JSObject::EXTERNAL_BYTE_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
|
| - return Builtins::builtin(
|
| - Builtins::KeyedStoreIC_ExternalUnsignedByteArray);
|
| - case JSObject::EXTERNAL_SHORT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
|
| - return Builtins::builtin(
|
| - Builtins::KeyedStoreIC_ExternalUnsignedShortArray);
|
| - case JSObject::EXTERNAL_INT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray);
|
| - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray);
|
| - case JSObject::EXTERNAL_FLOAT_ELEMENTS:
|
| - return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray);
|
| - default:
|
| - UNREACHABLE();
|
| - return NULL;
|
| - }
|
| -}
|
| -
|
| -
|
| static bool HasInterceptorGetter(JSObject* object) {
|
| return !object->GetNamedInterceptor()->getter()->IsUndefined();
|
| }
|
| @@ -482,6 +449,7 @@
|
|
|
|
|
| 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 +495,7 @@
|
|
|
| // 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 +544,142 @@
|
| }
|
|
|
|
|
| +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;
|
| +}
|
| +
|
| +
|
| +MaybeObject* CallICBase::ComputeMonomorphicStub(
|
| + LookupResult* lookup,
|
| + State state,
|
| + Code::ExtraICState extra_ic_state,
|
| + Handle<Object> object,
|
| + Handle<String> 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<JSObject> receiver = Handle<JSObject>::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> object,
|
| Handle<String> name) {
|
| // Bail out if we didn't find a result.
|
| @@ -594,90 +696,44 @@
|
| 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_);
|
| + 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 {
|
| - // 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<JSObject> receiver = Handle<JSObject>::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;
|
| - }
|
| + 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 +752,9 @@
|
| 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 +765,10 @@
|
| 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()) {
|
| @@ -764,6 +825,9 @@
|
| }
|
|
|
| if (FLAG_use_ic) {
|
| + Code* non_monomorphic_stub =
|
| + (state == UNINITIALIZED) ? pre_monomorphic_stub() : megamorphic_stub();
|
| +
|
| // Use specialized code for getting the length of strings and
|
| // string wrapper objects. The length property of string wrapper
|
| // objects is read-only and therefore always returns the length of
|
| @@ -771,22 +835,27 @@
|
| if ((object->IsString() || object->IsStringWrapper()) &&
|
| name->Equals(Heap::length_symbol())) {
|
| HandleScope scope;
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
|
| +#endif
|
| + if (state == PREMONOMORPHIC) {
|
| + if (object->IsString()) {
|
| + Map* map = HeapObject::cast(*object)->map();
|
| + const int offset = String::kLengthOffset;
|
| + PatchInlinedLoad(address(), map, offset);
|
| + set_target(Builtins::builtin(Builtins::LoadIC_StringLength));
|
| + } else {
|
| + set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
|
| + }
|
| + } else if (state == MONOMORPHIC && object->IsStringWrapper()) {
|
| + set_target(Builtins::builtin(Builtins::LoadIC_StringWrapperLength));
|
| + } else {
|
| + set_target(non_monomorphic_stub);
|
| + }
|
| // Get the string if we have a string wrapper object.
|
| if (object->IsJSValue()) {
|
| object = Handle<Object>(Handle<JSValue>::cast(object)->value());
|
| }
|
| -#ifdef DEBUG
|
| - if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
|
| -#endif
|
| - Map* map = HeapObject::cast(*object)->map();
|
| - if (object->IsString()) {
|
| - const int offset = String::kLengthOffset;
|
| - PatchInlinedLoad(address(), map, offset);
|
| - }
|
| -
|
| - Code* target = NULL;
|
| - target = Builtins::builtin(Builtins::LoadIC_StringLength);
|
| - set_target(target);
|
| return Smi::FromInt(String::cast(*object)->length());
|
| }
|
|
|
| @@ -795,12 +864,14 @@
|
| #ifdef DEBUG
|
| if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
|
| #endif
|
| - Map* map = HeapObject::cast(*object)->map();
|
| - const int offset = JSArray::kLengthOffset;
|
| - PatchInlinedLoad(address(), map, offset);
|
| -
|
| - Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
|
| - set_target(target);
|
| + if (state == PREMONOMORPHIC) {
|
| + Map* map = HeapObject::cast(*object)->map();
|
| + const int offset = JSArray::kLengthOffset;
|
| + PatchInlinedLoad(address(), map, offset);
|
| + set_target(Builtins::builtin(Builtins::LoadIC_ArrayLength));
|
| + } else {
|
| + set_target(non_monomorphic_stub);
|
| + }
|
| return JSArray::cast(*object)->length();
|
| }
|
|
|
| @@ -810,8 +881,11 @@
|
| #ifdef DEBUG
|
| if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
|
| #endif
|
| - Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
|
| - set_target(target);
|
| + if (state == PREMONOMORPHIC) {
|
| + set_target(Builtins::builtin(Builtins::LoadIC_FunctionPrototype));
|
| + } else {
|
| + set_target(non_monomorphic_stub);
|
| + }
|
| return Accessors::FunctionGetPrototype(*object, 0);
|
| }
|
| }
|
| @@ -1034,6 +1108,8 @@
|
| }
|
|
|
| if (FLAG_use_ic) {
|
| + // TODO(1073): don't ignore the current stub state.
|
| +
|
| // Use specialized code for getting the length of strings.
|
| if (object->IsString() && name->Equals(Heap::length_symbol())) {
|
| Handle<String> string = Handle<String>::cast(object);
|
| @@ -1131,20 +1207,31 @@
|
|
|
| if (use_ic) {
|
| Code* stub = generic_stub();
|
| - if (object->IsString() && key->IsNumber()) {
|
| - stub = string_stub();
|
| - } else if (object->IsJSObject()) {
|
| - Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| - if (receiver->HasExternalArrayElements()) {
|
| - stub = external_array_stub(receiver->GetElementsKind());
|
| - } else if (receiver->HasIndexedInterceptor()) {
|
| - stub = indexed_interceptor_stub();
|
| - } else if (state == UNINITIALIZED &&
|
| - key->IsSmi() &&
|
| - receiver->map()->has_fast_elements()) {
|
| - MaybeObject* probe = StubCache::ComputeKeyedLoadSpecialized(*receiver);
|
| - stub =
|
| - probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
|
| + if (state == UNINITIALIZED) {
|
| + if (object->IsString() && key->IsNumber()) {
|
| + stub = string_stub();
|
| + } else if (object->IsJSObject()) {
|
| + Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| + if (receiver->HasExternalArrayElements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver,
|
| + false);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + } else if (receiver->HasIndexedInterceptor()) {
|
| + stub = indexed_interceptor_stub();
|
| + } else if (receiver->HasPixelElements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedLoadPixelArray(*receiver);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + } else if (key->IsSmi() &&
|
| + receiver->map()->has_fast_elements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedLoadSpecialized(*receiver);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + }
|
| }
|
| }
|
| if (stub != NULL) set_target(stub);
|
| @@ -1284,6 +1371,7 @@
|
|
|
|
|
| MaybeObject* StoreIC::Store(State state,
|
| + Code::ExtraICState extra_ic_state,
|
| Handle<Object> object,
|
| Handle<String> name,
|
| Handle<Object> value) {
|
| @@ -1313,8 +1401,10 @@
|
| #ifdef DEBUG
|
| if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
|
| #endif
|
| - Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength);
|
| - set_target(target);
|
| + Builtins::Name target = (extra_ic_state == kStoreICStrict)
|
| + ? Builtins::StoreIC_ArrayLength_Strict
|
| + : Builtins::StoreIC_ArrayLength;
|
| + set_target(Builtins::builtin(target));
|
| return receiver->SetProperty(*name, *value, NONE);
|
| }
|
|
|
| @@ -1372,15 +1462,23 @@
|
|
|
| // If no inlined store ic was patched, generate a stub for this
|
| // store.
|
| - UpdateCaches(&lookup, state, receiver, name, value);
|
| + UpdateCaches(&lookup, state, extra_ic_state, receiver, name, value);
|
| + } else {
|
| + // Strict mode doesn't allow setting non-existent global property.
|
| + if (extra_ic_state == kStoreICStrict && IsContextual(object)) {
|
| + return ReferenceError("not_defined", name);
|
| + }
|
| }
|
| }
|
|
|
| if (receiver->IsJSGlobalProxy()) {
|
| // Generate a generic stub that goes to the runtime when we see a global
|
| // proxy as receiver.
|
| - if (target() != global_proxy_stub()) {
|
| - set_target(global_proxy_stub());
|
| + Code* stub = (extra_ic_state == kStoreICStrict)
|
| + ? global_proxy_stub_strict()
|
| + : global_proxy_stub();
|
| + if (target() != stub) {
|
| + set_target(stub);
|
| #ifdef DEBUG
|
| TraceIC("StoreIC", name, state, target());
|
| #endif
|
| @@ -1394,6 +1492,7 @@
|
|
|
| void StoreIC::UpdateCaches(LookupResult* lookup,
|
| State state,
|
| + Code::ExtraICState extra_ic_state,
|
| Handle<JSObject> receiver,
|
| Handle<String> name,
|
| Handle<Object> value) {
|
| @@ -1414,8 +1513,8 @@
|
| Object* code = NULL;
|
| switch (type) {
|
| case FIELD: {
|
| - maybe_code = StubCache::ComputeStoreField(*name, *receiver,
|
| - lookup->GetFieldIndex());
|
| + maybe_code = StubCache::ComputeStoreField(
|
| + *name, *receiver, lookup->GetFieldIndex(), NULL, extra_ic_state);
|
| break;
|
| }
|
| case MAP_TRANSITION: {
|
| @@ -1424,8 +1523,8 @@
|
| ASSERT(type == MAP_TRANSITION);
|
| Handle<Map> transition(lookup->GetTransitionMap());
|
| int index = transition->PropertyIndexFor(*name);
|
| - maybe_code = StubCache::ComputeStoreField(*name, *receiver,
|
| - index, *transition);
|
| + maybe_code = StubCache::ComputeStoreField(
|
| + *name, *receiver, index, *transition, extra_ic_state);
|
| break;
|
| }
|
| case NORMAL: {
|
| @@ -1436,10 +1535,11 @@
|
| Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
|
| JSGlobalPropertyCell* cell =
|
| JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
|
| - maybe_code = StubCache::ComputeStoreGlobal(*name, *global, cell);
|
| + maybe_code = StubCache::ComputeStoreGlobal(
|
| + *name, *global, cell, extra_ic_state);
|
| } else {
|
| if (lookup->holder() != *receiver) return;
|
| - maybe_code = StubCache::ComputeStoreNormal();
|
| + maybe_code = StubCache::ComputeStoreNormal(extra_ic_state);
|
| }
|
| break;
|
| }
|
| @@ -1447,12 +1547,14 @@
|
| if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
|
| AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
|
| if (v8::ToCData<Address>(callback->setter()) == 0) return;
|
| - maybe_code = StubCache::ComputeStoreCallback(*name, *receiver, callback);
|
| + maybe_code = StubCache::ComputeStoreCallback(
|
| + *name, *receiver, callback, extra_ic_state);
|
| break;
|
| }
|
| case INTERCEPTOR: {
|
| ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined());
|
| - maybe_code = StubCache::ComputeStoreInterceptor(*name, *receiver);
|
| + maybe_code = StubCache::ComputeStoreInterceptor(
|
| + *name, *receiver, extra_ic_state);
|
| break;
|
| }
|
| default:
|
| @@ -1468,7 +1570,11 @@
|
| set_target(Code::cast(code));
|
| } else if (state == MONOMORPHIC) {
|
| // Only move to megamorphic if the target changes.
|
| - if (target() != Code::cast(code)) set_target(megamorphic_stub());
|
| + if (target() != Code::cast(code)) {
|
| + set_target(extra_ic_state == kStoreICStrict
|
| + ? megamorphic_stub_strict()
|
| + : megamorphic_stub());
|
| + }
|
| } else if (state == MEGAMORPHIC) {
|
| // Update the stub cache.
|
| StubCache::Set(*name, receiver->map(), Code::cast(code));
|
| @@ -1526,16 +1632,25 @@
|
|
|
| if (use_ic) {
|
| Code* stub = generic_stub();
|
| - if (object->IsJSObject()) {
|
| - Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| - if (receiver->HasExternalArrayElements()) {
|
| - stub = external_array_stub(receiver->GetElementsKind());
|
| - } else if (state == UNINITIALIZED &&
|
| - key->IsSmi() &&
|
| - receiver->map()->has_fast_elements()) {
|
| - MaybeObject* probe = StubCache::ComputeKeyedStoreSpecialized(*receiver);
|
| - stub =
|
| - probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked());
|
| + if (state == UNINITIALIZED) {
|
| + if (object->IsJSObject()) {
|
| + Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| + if (receiver->HasExternalArrayElements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, true);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + } else if (receiver->HasPixelElements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedStorePixelArray(*receiver);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + } else if (key->IsSmi() && receiver->map()->has_fast_elements()) {
|
| + MaybeObject* probe =
|
| + StubCache::ComputeKeyedStoreSpecialized(*receiver);
|
| + stub = probe->IsFailure() ?
|
| + NULL : Code::cast(probe->ToObjectUnchecked());
|
| + }
|
| }
|
| }
|
| if (stub != NULL) set_target(stub);
|
| @@ -1641,11 +1756,13 @@
|
| 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
|
| @@ -1706,8 +1823,9 @@
|
| ASSERT(args.length() == 3);
|
| StoreIC ic;
|
| IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
|
| - return ic.Store(state, args.at<Object>(0), args.at<String>(1),
|
| - args.at<Object>(2));
|
| + Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
|
| + return ic.Store(state, extra_ic_state, args.at<Object>(0),
|
| + args.at<String>(1), args.at<Object>(2));
|
| }
|
|
|
|
|
| @@ -1972,6 +2090,8 @@
|
| }
|
|
|
| if (left_type.IsInteger32() && right_type.IsInteger32()) {
|
| + // Platforms with 32-bit Smis have no distinct INT32 type.
|
| + if (kSmiValueSize == 32) return SMI;
|
| return INT32;
|
| }
|
|
|
| @@ -2015,9 +2135,11 @@
|
| }
|
| if (type == TRBinaryOpIC::SMI &&
|
| previous_type == TRBinaryOpIC::SMI) {
|
| - if (op == Token::DIV || op == Token::MUL) {
|
| + if (op == Token::DIV || op == Token::MUL || kSmiValueSize == 32) {
|
| // Arithmetic on two Smi inputs has yielded a heap number.
|
| // That is the only way to get here from the Smi stub.
|
| + // With 32-bit Smis, all overflows give heap numbers, but with
|
| + // 31-bit Smis, most operations overflow to int32 results.
|
| result_type = TRBinaryOpIC::HEAP_NUMBER;
|
| } else {
|
| // Other operations on SMIs that overflow yield int32s.
|
| @@ -2032,8 +2154,6 @@
|
|
|
| Handle<Code> code = GetTypeRecordingBinaryOpStub(key, type, result_type);
|
| if (!code.is_null()) {
|
| - TRBinaryOpIC ic;
|
| - ic.patch(*code);
|
| if (FLAG_trace_ic) {
|
| PrintF("[TypeRecordingBinaryOpIC (%s->(%s->%s))#%s]\n",
|
| TRBinaryOpIC::GetName(previous_type),
|
| @@ -2041,6 +2161,8 @@
|
| TRBinaryOpIC::GetName(result_type),
|
| Token::Name(op));
|
| }
|
| + TRBinaryOpIC ic;
|
| + ic.patch(*code);
|
|
|
| // Activate inlined smi code.
|
| if (previous_type == TRBinaryOpIC::UNINITIALIZED) {
|
|
|