diff options
Diffstat (limited to 'deps/v8/src/ic.cc')
-rw-r--r-- | deps/v8/src/ic.cc | 402 |
1 files changed, 212 insertions, 190 deletions
diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index 1e7997a80d..a327173629 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -127,6 +127,11 @@ IC::IC(FrameDepth depth, Isolate* isolate) // running DeltaBlue and a ~25% speedup of gbemu with the '--nouse-ic' flag. const Address entry = Isolate::c_entry_fp(isolate->thread_local_top()); + Address constant_pool = NULL; + if (FLAG_enable_ool_constant_pool) { + constant_pool = Memory::Address_at( + entry + ExitFrameConstants::kConstantPoolOffset); + } Address* pc_address = reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset); Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset); @@ -134,6 +139,10 @@ IC::IC(FrameDepth depth, Isolate* isolate) // StubFailureTrampoline, we need to look one frame further down the stack to // find the frame pointer and the return address stack slot. if (depth == EXTRA_CALL_FRAME) { + if (FLAG_enable_ool_constant_pool) { + constant_pool = Memory::Address_at( + fp + StandardFrameConstants::kConstantPoolOffset); + } const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset; pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset); fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset); @@ -145,18 +154,20 @@ IC::IC(FrameDepth depth, Isolate* isolate) ASSERT(fp == frame->fp() && pc_address == frame->pc_address()); #endif fp_ = fp; + if (FLAG_enable_ool_constant_pool) { + raw_constant_pool_ = handle( + ConstantPoolArray::cast(reinterpret_cast<Object*>(constant_pool)), + isolate); + } pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address); target_ = handle(raw_target(), isolate); state_ = target_->ic_state(); - extra_ic_state_ = target_->needs_extended_extra_ic_state(target_->kind()) - ? target_->extended_extra_ic_state() - : target_->extra_ic_state(); + extra_ic_state_ = target_->extra_ic_state(); } #ifdef ENABLE_DEBUGGER_SUPPORT -Address IC::OriginalCodeAddress() const { - HandleScope scope(isolate()); +SharedFunctionInfo* IC::GetSharedFunctionInfo() const { // Compute the JavaScript frame for the frame pointer of this IC // structure. We need this to be able to find the function // corresponding to the frame. @@ -166,21 +177,25 @@ Address IC::OriginalCodeAddress() const { // Find the function on the stack and both the active code for the // function and the original code. JSFunction* function = frame->function(); - Handle<SharedFunctionInfo> shared(function->shared(), isolate()); + return function->shared(); +} + + +Code* IC::GetCode() const { + HandleScope scope(isolate()); + Handle<SharedFunctionInfo> shared(GetSharedFunctionInfo(), isolate()); Code* code = shared->code(); + return code; +} + + +Code* IC::GetOriginalCode() const { + HandleScope scope(isolate()); + Handle<SharedFunctionInfo> shared(GetSharedFunctionInfo(), isolate()); ASSERT(Debug::HasDebugInfo(shared)); Code* original_code = Debug::GetDebugInfo(shared)->original_code(); ASSERT(original_code->IsCode()); - // Get the address of the call site in the active code. This is the - // place where the call to DebugBreakXXX is and where the IC - // normally would be. - Address addr = Assembler::target_address_from_return_address(pc()); - // Return the address in the original code. This is the place where - // the call which has been overwritten by the DebugBreakXXX resides - // and the place where the inline cache system should look. - intptr_t delta = - original_code->instruction_start() - code->instruction_start(); - return addr + delta; + return original_code; } #endif @@ -411,21 +426,26 @@ void IC::PostPatching(Address address, Code* target, Code* old_target) { } -void IC::Clear(Isolate* isolate, Address address) { - Code* target = GetTargetAtAddress(address); +void IC::Clear(Isolate* isolate, Address address, + ConstantPoolArray* constant_pool) { + Code* target = GetTargetAtAddress(address, constant_pool); // Don't clear debug break inline cache as it will remove the break point. if (target->is_debug_stub()) return; switch (target->kind()) { - case Code::LOAD_IC: return LoadIC::Clear(isolate, address, target); + case Code::LOAD_IC: + return LoadIC::Clear(isolate, address, target, constant_pool); case Code::KEYED_LOAD_IC: - return KeyedLoadIC::Clear(isolate, address, target); - case Code::STORE_IC: return StoreIC::Clear(isolate, address, target); + return KeyedLoadIC::Clear(isolate, address, target, constant_pool); + case Code::STORE_IC: + return StoreIC::Clear(isolate, address, target, constant_pool); case Code::KEYED_STORE_IC: - return KeyedStoreIC::Clear(isolate, address, target); - case Code::COMPARE_IC: return CompareIC::Clear(isolate, address, target); - case Code::COMPARE_NIL_IC: return CompareNilIC::Clear(address, target); + return KeyedStoreIC::Clear(isolate, address, target, constant_pool); + case Code::COMPARE_IC: + return CompareIC::Clear(isolate, address, target, constant_pool); + case Code::COMPARE_NIL_IC: + return CompareNilIC::Clear(address, target, constant_pool); case Code::BINARY_OP_IC: case Code::TO_BOOLEAN_IC: // Clearing these is tricky and does not @@ -436,40 +456,56 @@ void IC::Clear(Isolate* isolate, Address address) { } -void KeyedLoadIC::Clear(Isolate* isolate, Address address, Code* target) { +void KeyedLoadIC::Clear(Isolate* isolate, + Address address, + Code* target, + ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; // Make sure to also clear the map used in inline fast cases. If we // do not clear these maps, cached code can keep objects alive // through the embedded maps. - SetTargetAtAddress(address, *pre_monomorphic_stub(isolate)); + SetTargetAtAddress(address, *pre_monomorphic_stub(isolate), constant_pool); } -void LoadIC::Clear(Isolate* isolate, Address address, Code* target) { +void LoadIC::Clear(Isolate* isolate, + Address address, + Code* target, + ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; Code* code = target->GetIsolate()->stub_cache()->FindPreMonomorphicIC( Code::LOAD_IC, target->extra_ic_state()); - SetTargetAtAddress(address, code); + SetTargetAtAddress(address, code, constant_pool); } -void StoreIC::Clear(Isolate* isolate, Address address, Code* target) { +void StoreIC::Clear(Isolate* isolate, + Address address, + Code* target, + ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; Code* code = target->GetIsolate()->stub_cache()->FindPreMonomorphicIC( Code::STORE_IC, target->extra_ic_state()); - SetTargetAtAddress(address, code); + SetTargetAtAddress(address, code, constant_pool); } -void KeyedStoreIC::Clear(Isolate* isolate, Address address, Code* target) { +void KeyedStoreIC::Clear(Isolate* isolate, + Address address, + Code* target, + ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; SetTargetAtAddress(address, *pre_monomorphic_stub( - isolate, StoreIC::GetStrictMode(target->extra_ic_state()))); + isolate, StoreIC::GetStrictMode(target->extra_ic_state())), + constant_pool); } -void CompareIC::Clear(Isolate* isolate, Address address, Code* target) { +void CompareIC::Clear(Isolate* isolate, + Address address, + Code* target, + ConstantPoolArray* constant_pool) { ASSERT(target->major_key() == CodeStub::CompareIC); CompareIC::State handler_state; Token::Value op; @@ -477,7 +513,7 @@ void CompareIC::Clear(Isolate* isolate, Address address, Code* target) { &handler_state, &op); // Only clear CompareICs that can retain objects. if (handler_state != KNOWN_OBJECT) return; - SetTargetAtAddress(address, GetRawUninitialized(isolate, op)); + SetTargetAtAddress(address, GetRawUninitialized(isolate, op), constant_pool); PatchInlinedSmiCode(address, DISABLE_INLINED_SMI_CHECK); } @@ -500,31 +536,6 @@ MaybeObject* LoadIC::Load(Handle<Object> object, } if (FLAG_use_ic) { - // 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 - // the underlying string value. See ECMA-262 15.5.5.1. - if (object->IsStringWrapper() && - name->Equals(isolate()->heap()->length_string())) { - Handle<Code> stub; - if (state() == UNINITIALIZED) { - stub = pre_monomorphic_stub(); - } else if (state() == PREMONOMORPHIC || state() == MONOMORPHIC) { - StringLengthStub string_length_stub(kind()); - stub = string_length_stub.GetCode(isolate()); - } else if (state() != MEGAMORPHIC) { - ASSERT(state() != GENERIC); - stub = megamorphic_stub(); - } - if (!stub.is_null()) { - set_target(*stub); - if (FLAG_trace_ic) PrintF("[LoadIC : +#length /stringwrapper]\n"); - } - // Get the string if we have a string wrapper object. - String* string = String::cast(JSValue::cast(*object)->value()); - return Smi::FromInt(string->length()); - } - // Use specialized code for getting prototype of functions. if (object->IsJSFunction() && name->Equals(isolate()->heap()->prototype_string()) && @@ -553,7 +564,10 @@ MaybeObject* LoadIC::Load(Handle<Object> object, if (kind() == Code::KEYED_LOAD_IC && name->AsArrayIndex(&index)) { // Rewrite to the generic keyed load stub. if (FLAG_use_ic) set_target(*generic_stub()); - return Runtime::GetElementOrCharAtOrFail(isolate(), object, index); + Handle<Object> result = + Runtime::GetElementOrCharAt(isolate(), object, index); + RETURN_IF_EMPTY_HANDLE(isolate(), result); + return *result; } bool use_ic = MigrateDeprecated(object) ? false : FLAG_use_ic; @@ -610,28 +624,33 @@ bool IC::UpdatePolymorphicIC(Handle<HeapType> type, TypeHandleList types; CodeHandleList handlers; - int number_of_valid_types; - int handler_to_overwrite = -1; - target()->FindAllTypes(&types); int number_of_types = types.length(); - number_of_valid_types = number_of_types; + int deprecated_types = 0; + int handler_to_overwrite = -1; for (int i = 0; i < number_of_types; i++) { Handle<HeapType> current_type = types.at(i); - // Filter out deprecated maps to ensure their instances get migrated. if (current_type->IsClass() && current_type->AsClass()->is_deprecated()) { - number_of_valid_types--; - // If the receiver type is already in the polymorphic IC, this indicates - // there was a prototoype chain failure. In that case, just overwrite the - // handler. + // Filter out deprecated maps to ensure their instances get migrated. + ++deprecated_types; } else if (type->IsCurrently(current_type)) { - ASSERT(handler_to_overwrite == -1); - number_of_valid_types--; + // If the receiver type is already in the polymorphic IC, this indicates + // there was a prototoype chain failure. In that case, just overwrite the + // handler. + handler_to_overwrite = i; + } else if (handler_to_overwrite == -1 && + current_type->IsClass() && + type->IsClass() && + IsTransitionOfMonomorphicTarget(*current_type->AsClass(), + *type->AsClass())) { handler_to_overwrite = i; } } + int number_of_valid_types = + number_of_types - deprecated_types - (handler_to_overwrite != -1); + if (number_of_valid_types >= 4) return false; if (number_of_types == 0) return false; if (!target()->FindHandlers(&handlers, types.length())) return false; @@ -639,13 +658,16 @@ bool IC::UpdatePolymorphicIC(Handle<HeapType> type, number_of_valid_types++; if (handler_to_overwrite >= 0) { handlers.Set(handler_to_overwrite, code); + if (!type->IsCurrently(types.at(handler_to_overwrite))) { + types.Set(handler_to_overwrite, type); + } } else { types.Add(type); handlers.Add(code); } Handle<Code> ic = isolate()->stub_cache()->ComputePolymorphicIC( - &types, &handlers, number_of_valid_types, name, extra_ic_state()); + kind(), &types, &handlers, number_of_valid_types, name, extra_ic_state()); set_target(*ic); return true; } @@ -697,7 +719,7 @@ void IC::UpdateMonomorphicIC(Handle<HeapType> type, Handle<String> name) { if (!handler->is_handler()) return set_target(*handler); Handle<Code> ic = isolate()->stub_cache()->ComputeMonomorphicIC( - name, type, handler, extra_ic_state()); + kind(), name, type, handler, extra_ic_state()); set_target(*ic); } @@ -713,19 +735,18 @@ void IC::CopyICToMegamorphicCache(Handle<String> name) { } -bool IC::IsTransitionOfMonomorphicTarget(Handle<HeapType> type) { - if (!type->IsClass()) return false; - Map* receiver_map = *type->AsClass(); - Map* current_map = target()->FindFirstMap(); - ElementsKind receiver_elements_kind = receiver_map->elements_kind(); +bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) { + if (source_map == NULL) return true; + if (target_map == NULL) return false; + ElementsKind target_elements_kind = target_map->elements_kind(); bool more_general_transition = IsMoreGeneralElementsKindTransition( - current_map->elements_kind(), receiver_elements_kind); + source_map->elements_kind(), target_elements_kind); Map* transitioned_map = more_general_transition - ? current_map->LookupElementsTransitionMap(receiver_elements_kind) + ? source_map->LookupElementsTransitionMap(target_elements_kind) : NULL; - return transitioned_map == receiver_map; + return transitioned_map == target_map; } @@ -738,17 +759,7 @@ void IC::PatchCache(Handle<HeapType> type, case MONOMORPHIC_PROTOTYPE_FAILURE: UpdateMonomorphicIC(type, code, name); break; - case MONOMORPHIC: { - // For now, call stubs are allowed to rewrite to the same stub. This - // happens e.g., when the field does not contain a function. - ASSERT(!target().is_identical_to(code)); - Code* old_handler = target()->FindFirstHandler(); - if (old_handler == *code && IsTransitionOfMonomorphicTarget(type)) { - UpdateMonomorphicIC(type, code, name); - break; - } - // Fall through. - } + case MONOMORPHIC: // Fall through. case POLYMORPHIC: if (!target()->is_keyed_stub()) { if (UpdatePolymorphicIC(type, name, code)) break; @@ -847,8 +858,11 @@ Handle<Code> IC::ComputeHandler(LookupResult* lookup, isolate(), *object, cache_holder)); Handle<Code> code = isolate()->stub_cache()->FindHandler( - name, handle(stub_holder->map()), kind(), cache_holder); - if (!code.is_null()) return code; + name, handle(stub_holder->map()), kind(), cache_holder, + lookup->holder()->HasFastProperties() ? Code::FAST : Code::NORMAL); + if (!code.is_null()) { + return code; + } code = CompileHandler(lookup, object, name, value, cache_holder); ASSERT(code->is_handler()); @@ -871,6 +885,17 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, return SimpleFieldLoad(length_index); } + if (object->IsStringWrapper() && + name->Equals(isolate()->heap()->length_string())) { + if (kind() == Code::LOAD_IC) { + StringLengthStub string_length_stub; + return string_length_stub.GetCode(isolate()); + } else { + KeyedStringLengthStub string_length_stub; + return string_length_stub.GetCode(isolate()); + } + } + Handle<HeapType> type = CurrentTypeOf(object, isolate()); Handle<JSObject> holder(lookup->holder()); LoadStubCompiler compiler(isolate(), kNoExtraICState, cache_holder, kind()); @@ -942,8 +967,8 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<JSFunction> function = Handle<JSFunction>::cast(getter); if (!object->IsJSObject() && !function->IsBuiltin() && - function->shared()->is_classic_mode()) { - // Calling non-strict non-builtins with a value as the receiver + function->shared()->strict_mode() == SLOPPY) { + // Calling sloppy non-builtins with a value as the receiver // requires boxing. break; } @@ -1063,26 +1088,25 @@ MaybeObject* KeyedLoadIC::Load(Handle<Object> object, Handle<Object> key) { MaybeObject* maybe_object = NULL; Handle<Code> stub = generic_stub(); - // Check for values that can be converted into an internalized string directly - // or is representable as a smi. + // Check for non-string values that can be converted into an + // internalized string directly or is representable as a smi. key = TryConvertKey(key, isolate()); if (key->IsInternalizedString()) { maybe_object = LoadIC::Load(object, Handle<String>::cast(key)); if (maybe_object->IsFailure()) return maybe_object; } else if (FLAG_use_ic && !object->IsAccessCheckNeeded()) { - ASSERT(!object->IsJSGlobalProxy()); if (object->IsString() && key->IsNumber()) { if (state() == UNINITIALIZED) stub = string_stub(); } else if (object->IsJSObject()) { Handle<JSObject> receiver = Handle<JSObject>::cast(object); if (receiver->elements()->map() == - isolate()->heap()->non_strict_arguments_elements_map()) { - stub = non_strict_arguments_stub(); + isolate()->heap()->sloppy_arguments_elements_map()) { + stub = sloppy_arguments_stub(); } else if (receiver->HasIndexedInterceptor()) { stub = indexed_interceptor_stub(); } else if (!key->ToSmi()->IsFailure() && - (!target().is_identical_to(non_strict_arguments_stub()))) { + (!target().is_identical_to(sloppy_arguments_stub()))) { stub = LoadElementStub(receiver); } } @@ -1092,7 +1116,6 @@ MaybeObject* KeyedLoadIC::Load(Handle<Object> object, Handle<Object> key) { if (*stub == *generic_stub()) { TRACE_GENERIC_IC(isolate(), "KeyedLoadIC", "set generic"); } - ASSERT(!stub.is_null()); set_target(*stub); TRACE_IC("LoadIC", key); } @@ -1110,22 +1133,20 @@ static bool LookupForWrite(Handle<JSObject> receiver, Handle<JSObject> holder = receiver; receiver->Lookup(*name, lookup); if (lookup->IsFound()) { - if (lookup->IsReadOnly() || !lookup->IsCacheable()) return false; - - if (lookup->holder() == *receiver) { - if (lookup->IsInterceptor() && !HasInterceptorSetter(*receiver)) { - receiver->LocalLookupRealNamedProperty(*name, lookup); - return lookup->IsFound() && - !lookup->IsReadOnly() && - lookup->CanHoldValue(value) && - lookup->IsCacheable(); - } - return lookup->CanHoldValue(value); + if (lookup->IsInterceptor() && !HasInterceptorSetter(lookup->holder())) { + receiver->LocalLookupRealNamedProperty(*name, lookup); + if (!lookup->IsFound()) return false; } + if (lookup->IsReadOnly() || !lookup->IsCacheable()) return false; + if (lookup->holder() == *receiver) return lookup->CanHoldValue(value); if (lookup->IsPropertyCallbacks()) return true; - // JSGlobalProxy always goes via the runtime, so it's safe to cache. - if (receiver->IsJSGlobalProxy()) return true; + // JSGlobalProxy either stores on the global object in the prototype, or + // goes into the runtime if access checks are needed, so this is always + // safe. + if (receiver->IsJSGlobalProxy()) { + return lookup->holder() == receiver->GetPrototype(); + } // Currently normal holders in the prototype chain are not supported. They // would require a runtime positive lookup and verification that the details // have not changed. @@ -1183,7 +1204,7 @@ MaybeObject* StoreIC::Store(Handle<Object> object, } // The length property of string values is read-only. Throw in strict mode. - if (strict_mode() == kStrictMode && object->IsString() && + if (strict_mode() == STRICT && object->IsString() && name->Equals(isolate()->heap()->length_string())) { return TypeError("strict_read_only_property", object, name); } @@ -1204,27 +1225,7 @@ MaybeObject* StoreIC::Store(Handle<Object> object, } // Observed objects are always modified through the runtime. - if (FLAG_harmony_observation && receiver->map()->is_observed()) { - Handle<Object> result = JSReceiver::SetProperty( - receiver, name, value, NONE, strict_mode(), store_mode); - RETURN_IF_EMPTY_HANDLE(isolate(), result); - return *result; - } - - // Use specialized code for setting the length of arrays with fast - // properties. Slow properties might indicate redefinition of the length - // property. Note that when redefined using Object.freeze, it's possible - // to have fast properties but a read-only length. - if (FLAG_use_ic && - receiver->IsJSArray() && - name->Equals(isolate()->heap()->length_string()) && - Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() && - receiver->HasFastProperties() && - !receiver->map()->is_frozen()) { - Handle<Code> stub = - StoreArrayLengthStub(kind(), strict_mode()).GetCode(isolate()); - set_target(*stub); - TRACE_IC("StoreIC", name); + if (receiver->map()->is_observed()) { Handle<Object> result = JSReceiver::SetProperty( receiver, name, value, NONE, strict_mode(), store_mode); RETURN_IF_EMPTY_HANDLE(isolate(), result); @@ -1234,7 +1235,7 @@ MaybeObject* StoreIC::Store(Handle<Object> object, LookupResult lookup(isolate()); bool can_store = LookupForWrite(receiver, name, value, &lookup, this); if (!can_store && - strict_mode() == kStrictMode && + strict_mode() == STRICT && !(lookup.IsProperty() && lookup.IsReadOnly()) && object->IsGlobalObject()) { // Strict mode doesn't allow setting non-existent global property. @@ -1264,7 +1265,7 @@ MaybeObject* StoreIC::Store(Handle<Object> object, Handle<Code> StoreIC::initialize_stub(Isolate* isolate, - StrictModeFlag strict_mode) { + StrictMode strict_mode) { ExtraICState extra_state = ComputeExtraICState(strict_mode); Handle<Code> ic = isolate->stub_cache()->ComputeStore( UNINITIALIZED, extra_state); @@ -1283,7 +1284,7 @@ Handle<Code> StoreIC::generic_stub() const { Handle<Code> StoreIC::pre_monomorphic_stub(Isolate* isolate, - StrictModeFlag strict_mode) { + StrictMode strict_mode) { ExtraICState state = ComputeExtraICState(strict_mode); return isolate->stub_cache()->ComputeStore(PREMONOMORPHIC, state); } @@ -1310,14 +1311,14 @@ Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, Handle<String> name, Handle<Object> value, InlineCacheHolderFlag cache_holder) { - if (object->IsJSGlobalProxy()) return slow_stub(); + if (object->IsAccessCheckNeeded()) return slow_stub(); ASSERT(cache_holder == OWN_MAP); // This is currently guaranteed by checks in StoreIC::Store. Handle<JSObject> receiver = Handle<JSObject>::cast(object); Handle<JSObject> holder(lookup->holder()); // Handlers do not use strict mode. - StoreStubCompiler compiler(isolate(), kNonStrictMode, kind()); + StoreStubCompiler compiler(isolate(), SLOPPY, kind()); switch (lookup->type()) { case FIELD: return compiler.CompileStoreField(receiver, lookup, name); @@ -1334,17 +1335,19 @@ Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, } case NORMAL: if (kind() == Code::KEYED_STORE_IC) break; - if (receiver->IsGlobalObject()) { + if (receiver->IsJSGlobalProxy() || receiver->IsGlobalObject()) { // The stub generated for the global object picks the value directly // from the property cell. So the property must be directly on the // global object. - Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver); + Handle<GlobalObject> global = receiver->IsJSGlobalProxy() + ? handle(GlobalObject::cast(receiver->GetPrototype())) + : Handle<GlobalObject>::cast(receiver); Handle<PropertyCell> cell(global->GetPropertyCell(lookup), isolate()); Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value); - StoreGlobalStub stub(union_type->IsConstant()); - + StoreGlobalStub stub( + union_type->IsConstant(), receiver->IsJSGlobalProxy()); Handle<Code> code = stub.GetCodeCopyFromTemplate( - isolate(), receiver->map(), *cell); + isolate(), global, cell); // TODO(verwaest): Move caching of these NORMAL stubs outside as well. HeapObject::UpdateMapCodeCache(receiver, name, code); return code; @@ -1352,7 +1355,6 @@ Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, ASSERT(holder.is_identical_to(receiver)); return isolate()->builtins()->StoreIC_Normal(); case CALLBACKS: { - if (kind() == Code::KEYED_STORE_IC) break; Handle<Object> callback(lookup->GetCallbackObject(), isolate()); if (callback->IsExecutableAccessorInfo()) { Handle<ExecutableAccessorInfo> info = @@ -1380,12 +1382,23 @@ Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, // TODO(dcarney): Handle correctly. if (callback->IsDeclaredAccessorInfo()) break; ASSERT(callback->IsForeign()); + + // Use specialized code for setting the length of arrays with fast + // properties. Slow properties might indicate redefinition of the length + // property. + if (receiver->IsJSArray() && + name->Equals(isolate()->heap()->length_string()) && + Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() && + receiver->HasFastProperties()) { + return compiler.CompileStoreArrayLength(receiver, lookup, name); + } + // No IC support for old-style native accessors. break; } case INTERCEPTOR: if (kind() == Code::KEYED_STORE_IC) break; - ASSERT(HasInterceptorSetter(*receiver)); + ASSERT(HasInterceptorSetter(*holder)); return compiler.CompileStoreInterceptor(receiver, name); case CONSTANT: break; @@ -1439,9 +1452,10 @@ Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver, if (IsTransitionStoreMode(store_mode)) { transitioned_receiver_map = ComputeTransitionedMap(receiver, store_mode); } - if (receiver_map.is_identical_to(previous_receiver_map) || - IsTransitionOfMonomorphicTarget( - MapToType<HeapType>(transitioned_receiver_map, isolate()))) { + if ((receiver_map.is_identical_to(previous_receiver_map) && + IsTransitionStoreMode(store_mode)) || + IsTransitionOfMonomorphicTarget(*previous_receiver_map, + *transitioned_receiver_map)) { // If the "old" and "new" maps are in the same elements map family, or // if they at least come from the same origin for a transitioning store, // stay MONOMORPHIC and use the map for the most generic ElementsKind. @@ -1575,7 +1589,10 @@ KeyedAccessStoreMode KeyedStoreIC::GetStoreMode(Handle<JSObject> receiver, key->ToSmi()->To(&smi_key); int index = smi_key->value(); bool oob_access = IsOutOfBoundsAccess(receiver, index); - bool allow_growth = receiver->IsJSArray() && oob_access; + // Don't consider this a growing store if the store would send the receiver to + // dictionary mode. + bool allow_growth = receiver->IsJSArray() && oob_access && + !receiver->WouldConvertToSlowElements(key); if (allow_growth) { // Handle growing array in stub if necessary. if (receiver->HasFastSmiElements()) { @@ -1655,8 +1672,8 @@ MaybeObject* KeyedStoreIC::Store(Handle<Object> object, return *result; } - // Check for values that can be converted into an internalized string directly - // or is representable as a smi. + // Check for non-string values that can be converted into an + // internalized string directly or is representable as a smi. key = TryConvertKey(key, isolate()); MaybeObject* maybe_object = NULL; @@ -1669,8 +1686,10 @@ MaybeObject* KeyedStoreIC::Store(Handle<Object> object, JSReceiver::MAY_BE_STORE_FROM_KEYED); if (maybe_object->IsFailure()) return maybe_object; } else { - bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded() && - !(FLAG_harmony_observation && object->IsJSObject() && + bool use_ic = FLAG_use_ic && + !object->IsAccessCheckNeeded() && + !object->IsJSGlobalProxy() && + !(object->IsJSObject() && JSObject::cast(*object)->map()->is_observed()); if (use_ic && !object->IsSmi()) { // Don't use ICs for maps of the objects in Array's prototype chain. We @@ -1681,16 +1700,18 @@ MaybeObject* KeyedStoreIC::Store(Handle<Object> object, } if (use_ic) { - ASSERT(!object->IsJSGlobalProxy()); + ASSERT(!object->IsAccessCheckNeeded()); if (object->IsJSObject()) { Handle<JSObject> receiver = Handle<JSObject>::cast(object); bool key_is_smi_like = key->IsSmi() || !key->ToSmi()->IsFailure(); if (receiver->elements()->map() == - isolate()->heap()->non_strict_arguments_elements_map()) { - stub = non_strict_arguments_stub(); + isolate()->heap()->sloppy_arguments_elements_map()) { + if (strict_mode() == SLOPPY) { + stub = sloppy_arguments_stub(); + } } else if (key_is_smi_like && - !(target().is_identical_to(non_strict_arguments_stub()))) { + !(target().is_identical_to(sloppy_arguments_stub()))) { // We should go generic if receiver isn't a dictionary, but our // prototype chain does have dictionary elements. This ensures that // other non-dictionary receivers in the polymorphic case benefit @@ -1791,11 +1812,11 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure) { RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) { - SealHandleScope shs(isolate); + HandleScope scope(isolate); ASSERT(args.length() == 2); - JSArray* receiver = JSArray::cast(args[0]); - Object* len = args[1]; + Handle<JSArray> receiver = args.at<JSArray>(0); + Handle<Object> len = args.at<Object>(1); // The generated code should filter out non-Smis before we get here. ASSERT(len->IsSmi()); @@ -1807,11 +1828,9 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) { ASSERT(debug_lookup.IsPropertyCallbacks() && !debug_lookup.IsReadOnly()); #endif - Object* result; - MaybeObject* maybe_result = receiver->SetElementsLength(len); - if (!maybe_result->To(&result)) return maybe_result; - - return len; + RETURN_IF_EMPTY_HANDLE(isolate, + JSArray::SetElementsLength(receiver, len)); + return *len; } @@ -1843,14 +1862,12 @@ RUNTIME_FUNCTION(MaybeObject*, SharedStoreIC_ExtendStorage) { Object* to_store = value; - if (FLAG_track_double_fields) { - DescriptorArray* descriptors = transition->instance_descriptors(); - PropertyDetails details = descriptors->GetDetails(transition->LastAdded()); - if (details.representation().IsDouble()) { - MaybeObject* maybe_storage = - isolate->heap()->AllocateHeapNumber(value->Number()); - if (!maybe_storage->To(&to_store)) return maybe_storage; - } + DescriptorArray* descriptors = transition->instance_descriptors(); + PropertyDetails details = descriptors->GetDetails(transition->LastAdded()); + if (details.representation().IsDouble()) { + MaybeObject* maybe_storage = + isolate->heap()->AllocateHeapNumber(value->Number()); + if (!maybe_storage->To(&to_store)) return maybe_storage; } new_storage->set(old_storage->length(), to_store); @@ -1894,7 +1911,7 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_Slow) { Handle<Object> object = args.at<Object>(0); Handle<Object> key = args.at<Object>(1); Handle<Object> value = args.at<Object>(2); - StrictModeFlag strict_mode = ic.strict_mode(); + StrictMode strict_mode = ic.strict_mode(); Handle<Object> result = Runtime::SetObjectProperty(isolate, object, key, value, NONE, @@ -1911,7 +1928,7 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Slow) { Handle<Object> object = args.at<Object>(0); Handle<Object> key = args.at<Object>(1); Handle<Object> value = args.at<Object>(2); - StrictModeFlag strict_mode = ic.strict_mode(); + StrictMode strict_mode = ic.strict_mode(); Handle<Object> result = Runtime::SetObjectProperty(isolate, object, key, value, NONE, @@ -1929,7 +1946,7 @@ RUNTIME_FUNCTION(MaybeObject*, ElementsTransitionAndStoreIC_Miss) { Handle<Map> map = args.at<Map>(1); Handle<Object> key = args.at<Object>(2); Handle<Object> object = args.at<Object>(3); - StrictModeFlag strict_mode = ic.strict_mode(); + StrictMode strict_mode = ic.strict_mode(); if (object->IsJSObject()) { JSObject::TransitionElementsKind(Handle<JSObject>::cast(object), map->elements_kind()); @@ -2352,7 +2369,7 @@ const char* BinaryOpIC::State::KindToString(Kind kind) { Type* BinaryOpIC::State::KindToType(Kind kind, Zone* zone) { switch (kind) { case NONE: return Type::None(zone); - case SMI: return Type::Smi(zone); + case SMI: return Type::SignedSmall(zone); case INT32: return Type::Signed32(zone); case NUMBER: return Type::Number(zone); case STRING: return Type::String(zone); @@ -2366,7 +2383,7 @@ Type* BinaryOpIC::State::KindToType(Kind kind, Zone* zone) { MaybeObject* BinaryOpIC::Transition(Handle<AllocationSite> allocation_site, Handle<Object> left, Handle<Object> right) { - State state(target()->extended_extra_ic_state()); + State state(target()->extra_ic_state()); // Compute the actual result using the builtin for the binary operation. Object* builtin = isolate()->js_builtins_object()->javascript_builtin( @@ -2377,8 +2394,11 @@ MaybeObject* BinaryOpIC::Transition(Handle<AllocationSite> allocation_site, isolate(), function, left, 1, &right, &caught_exception); if (caught_exception) return Failure::Exception(); + // Execution::Call can execute arbitrary JavaScript, hence potentially + // update the state of this very IC, so we must update the stored state. + UpdateTarget(); // Compute the new state. - State old_state = state; + State old_state(target()->extra_ic_state()); state.Update(left, right, result); // Check if we have a string operation here. @@ -2495,7 +2515,7 @@ Type* CompareIC::StateToType( Handle<Map> map) { switch (state) { case CompareIC::UNINITIALIZED: return Type::None(zone); - case CompareIC::SMI: return Type::Smi(zone); + case CompareIC::SMI: return Type::SignedSmall(zone); case CompareIC::NUMBER: return Type::Number(zone); case CompareIC::STRING: return Type::String(zone); case CompareIC::INTERNALIZED_STRING: return Type::InternalizedString(zone); @@ -2680,9 +2700,11 @@ RUNTIME_FUNCTION(Code*, CompareIC_Miss) { } -void CompareNilIC::Clear(Address address, Code* target) { +void CompareNilIC::Clear(Address address, + Code* target, + ConstantPoolArray* constant_pool) { if (IsCleared(target)) return; - ExtraICState state = target->extended_extra_ic_state(); + ExtraICState state = target->extra_ic_state(); CompareNilICStub stub(state, HydrogenCodeStub::UNINITIALIZED); stub.ClearState(); @@ -2690,7 +2712,7 @@ void CompareNilIC::Clear(Address address, Code* target) { Code* code = NULL; CHECK(stub.FindCodeInCache(&code, target->GetIsolate())); - SetTargetAtAddress(address, code); + SetTargetAtAddress(address, code, constant_pool); } @@ -2704,7 +2726,7 @@ MaybeObject* CompareNilIC::DoCompareNilSlow(NilValue nil, MaybeObject* CompareNilIC::CompareNil(Handle<Object> object) { - ExtraICState extra_ic_state = target()->extended_extra_ic_state(); + ExtraICState extra_ic_state = target()->extra_ic_state(); CompareNilICStub stub(extra_ic_state); @@ -2788,7 +2810,7 @@ Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op) { MaybeObject* ToBooleanIC::ToBoolean(Handle<Object> object) { - ToBooleanStub stub(target()->extended_extra_ic_state()); + ToBooleanStub stub(target()->extra_ic_state()); bool to_boolean_value = stub.UpdateStatus(object); Handle<Code> code = stub.GetCode(isolate()); set_target(*code); |