diff options
author | Ujjwal Sharma <usharma1998@gmail.com> | 2019-03-15 18:35:06 +0530 |
---|---|---|
committer | Refael Ackermann <refack@gmail.com> | 2019-03-28 16:36:18 -0400 |
commit | f579e1194046c50f2e6bb54348d48c8e7d1a53cf (patch) | |
tree | 9125787c758358365f74f9fd9673c14f57e67870 /deps/v8/src/ic | |
parent | 2c73868b0471fbd4038f500d076df056cbf697fe (diff) | |
download | android-node-v8-f579e1194046c50f2e6bb54348d48c8e7d1a53cf.tar.gz android-node-v8-f579e1194046c50f2e6bb54348d48c8e7d1a53cf.tar.bz2 android-node-v8-f579e1194046c50f2e6bb54348d48c8e7d1a53cf.zip |
deps: update V8 to 7.4.288.13
PR-URL: https://github.com/nodejs/node/pull/26685
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michaƫl Zasso <targos@protonmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Diffstat (limited to 'deps/v8/src/ic')
-rw-r--r-- | deps/v8/src/ic/accessor-assembler.cc | 893 | ||||
-rw-r--r-- | deps/v8/src/ic/accessor-assembler.h | 46 | ||||
-rw-r--r-- | deps/v8/src/ic/handler-configuration.h | 4 | ||||
-rw-r--r-- | deps/v8/src/ic/ic.cc | 491 | ||||
-rw-r--r-- | deps/v8/src/ic/ic.h | 44 | ||||
-rw-r--r-- | deps/v8/src/ic/keyed-store-generic.cc | 94 | ||||
-rw-r--r-- | deps/v8/src/ic/stub-cache.cc | 9 | ||||
-rw-r--r-- | deps/v8/src/ic/stub-cache.h | 4 |
8 files changed, 934 insertions, 651 deletions
diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc index 0bbc9391b0..bc636e4164 100644 --- a/deps/v8/src/ic/accessor-assembler.cc +++ b/deps/v8/src/ic/accessor-assembler.cc @@ -99,84 +99,48 @@ TNode<MaybeObject> AccessorAssembler::TryMonomorphicCase( void AccessorAssembler::HandlePolymorphicCase( Node* receiver_map, TNode<WeakFixedArray> feedback, Label* if_handler, - TVariable<MaybeObject>* var_handler, Label* if_miss, - int min_feedback_capacity) { + TVariable<MaybeObject>* var_handler, Label* if_miss) { Comment("HandlePolymorphicCase"); DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep()); - // Deferred so the unrolled case can omit frame construction in bytecode - // handler. - Label loop(this, Label::kDeferred); - // Iterate {feedback} array. const int kEntrySize = 2; - // Loading feedback's length is delayed until we need it when looking past - // the first {min_feedback_capacity} (map, handler) pairs. - Node* length = nullptr; - CSA_ASSERT(this, SmiGreaterThanOrEqual( - LoadWeakFixedArrayLength(feedback), - SmiConstant(min_feedback_capacity * kEntrySize))); - - const int kUnrolledIterations = IC::kMaxPolymorphicMapCount; - for (int i = 0; i < kUnrolledIterations; i++) { - int map_index = i * kEntrySize; - int handler_index = i * kEntrySize + 1; - - if (i >= min_feedback_capacity) { - if (length == nullptr) length = LoadWeakFixedArrayLength(feedback); - GotoIf(SmiGreaterThanOrEqual(SmiConstant(handler_index), CAST(length)), - if_miss); - } + // Load the {feedback} array length. + TNode<IntPtrT> length = LoadAndUntagWeakFixedArrayLength(feedback); + CSA_ASSERT(this, IntPtrLessThanOrEqual(IntPtrConstant(1), length)); - Label next_entry(this); + // This is a hand-crafted loop that only compares against the {length} + // in the end, since we already know that we will have at least a single + // entry in the {feedback} array anyways. + TVARIABLE(IntPtrT, var_index, IntPtrConstant(0)); + Label loop(this, &var_index), loop_next(this); + Goto(&loop); + BIND(&loop); + { TNode<MaybeObject> maybe_cached_map = - LoadWeakFixedArrayElement(feedback, map_index); + LoadWeakFixedArrayElement(feedback, var_index.value()); CSA_ASSERT(this, IsWeakOrCleared(maybe_cached_map)); GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)), - &next_entry); + &loop_next); // Found, now call handler. TNode<MaybeObject> handler = - LoadWeakFixedArrayElement(feedback, handler_index); + LoadWeakFixedArrayElement(feedback, var_index.value(), kTaggedSize); *var_handler = handler; Goto(if_handler); - BIND(&next_entry); + BIND(&loop_next); + var_index = + Signed(IntPtrAdd(var_index.value(), IntPtrConstant(kEntrySize))); + Branch(IntPtrLessThan(var_index.value(), length), &loop, if_miss); } - Goto(&loop); - - // Loop from {kUnrolledIterations}*kEntrySize to {length}. - BIND(&loop); - Node* start_index = IntPtrConstant(kUnrolledIterations * kEntrySize); - Node* end_index = LoadAndUntagWeakFixedArrayLength(feedback); - BuildFastLoop( - start_index, end_index, - [this, receiver_map, feedback, if_handler, var_handler](Node* index) { - Label next_entry(this); - TNode<MaybeObject> maybe_cached_map = - LoadWeakFixedArrayElement(feedback, index); - CSA_ASSERT(this, IsWeakOrCleared(maybe_cached_map)); - GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)), - &next_entry); - - // Found, now call handler. - TNode<MaybeObject> handler = - LoadWeakFixedArrayElement(feedback, index, kTaggedSize); - *var_handler = handler; - Goto(if_handler); - - BIND(&next_entry); - }, - kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); - // The loop falls through if no handler was found. - Goto(if_miss); } void AccessorAssembler::HandleLoadICHandlerCase( const LoadICParameters* p, TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent, - ElementSupport support_elements) { + ElementSupport support_elements, LoadAccessMode access_mode) { Comment("have_handler"); VARIABLE(var_holder, MachineRepresentation::kTagged, p->holder); @@ -195,14 +159,15 @@ void AccessorAssembler::HandleLoadICHandlerCase( { HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(), handler, miss, exit_point, on_nonexistent, - support_elements); + support_elements, access_mode); } BIND(&try_proto_handler); { GotoIf(IsCodeMap(LoadMap(CAST(handler))), &call_handler); HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler, - &if_smi_handler, miss, exit_point, ic_mode); + &if_smi_handler, miss, exit_point, ic_mode, + access_mode); } BIND(&call_handler); @@ -276,8 +241,8 @@ void AccessorAssembler::HandleLoadAccessor( BIND(&load); Callable callable = CodeFactory::CallApiCallback(isolate()); TNode<IntPtrT> argc = IntPtrConstant(0); - exit_point->Return(CallStub(callable, nullptr, context, callback, argc, - data, api_holder.value(), p->receiver)); + exit_point->Return(CallStub(callable, context, callback, argc, data, + api_holder.value(), p->receiver)); } BIND(&runtime); @@ -343,7 +308,8 @@ TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType( void AccessorAssembler::HandleLoadICSmiHandlerCase( const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler, SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point, - OnNonExistent on_nonexistent, ElementSupport support_elements) { + OnNonExistent on_nonexistent, ElementSupport support_elements, + LoadAccessMode access_mode) { VARIABLE(var_double_value, MachineRepresentation::kFloat64); Label rebox_double(this, &var_double_value); @@ -354,8 +320,17 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( Label if_element(this), if_indexed_string(this), if_property(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)), &if_element); - Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)), - &if_indexed_string, &if_property); + + if (access_mode == LoadAccessMode::kHas) { + CSA_ASSERT(this, + WordNotEqual(handler_kind, + IntPtrConstant(LoadHandler::kIndexedString))); + Goto(&if_property); + } else { + Branch( + WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)), + &if_indexed_string, &if_property); + } BIND(&if_element); Comment("element_load"); @@ -370,7 +345,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( EmitElementLoad(holder, elements, elements_kind, intptr_index, is_jsarray_condition, &if_hole, &rebox_double, &var_double_value, &unimplemented_elements_kind, &if_oob, - miss, exit_point); + miss, exit_point, access_mode); BIND(&unimplemented_elements_kind); { @@ -404,41 +379,63 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( miss); BIND(&return_undefined); - exit_point->Return(UndefinedConstant()); + exit_point->Return(access_mode == LoadAccessMode::kHas + ? FalseConstant() + : UndefinedConstant()); } BIND(&if_hole); { Comment("convert hole"); + GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss); GotoIf(IsNoElementsProtectorCellInvalid(), miss); - exit_point->Return(UndefinedConstant()); + exit_point->Return(access_mode == LoadAccessMode::kHas + ? FalseConstant() + : UndefinedConstant()); } - BIND(&if_indexed_string); - { - Label if_oob(this, Label::kDeferred); - - Comment("indexed string"); - Node* intptr_index = TryToIntptr(p->name, miss); - Node* length = LoadStringLengthAsWord(holder); - GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob); - TNode<Int32T> code = StringCharCodeAt(holder, intptr_index); - TNode<String> result = StringFromSingleCharCode(code); - Return(result); - - BIND(&if_oob); - Node* allow_out_of_bounds = - IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word); - GotoIfNot(allow_out_of_bounds, miss); - GotoIf(IsNoElementsProtectorCellInvalid(), miss); - Return(UndefinedConstant()); + if (access_mode != LoadAccessMode::kHas) { + BIND(&if_indexed_string); + { + Label if_oob(this, Label::kDeferred); + + Comment("indexed string"); + Node* intptr_index = TryToIntptr(p->name, miss); + Node* length = LoadStringLengthAsWord(holder); + GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob); + TNode<Int32T> code = StringCharCodeAt(holder, intptr_index); + TNode<String> result = StringFromSingleCharCode(code); + Return(result); + + BIND(&if_oob); + Node* allow_out_of_bounds = + IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word); + GotoIfNot(allow_out_of_bounds, miss); + GotoIf(IsNoElementsProtectorCellInvalid(), miss); + Return(UndefinedConstant()); + } } BIND(&if_property); Comment("property_load"); } + if (access_mode == LoadAccessMode::kHas) { + HandleLoadICSmiHandlerHasNamedCase(p, holder, handler_kind, miss, + exit_point); + } else { + HandleLoadICSmiHandlerLoadNamedCase( + p, holder, handler_kind, handler_word, &rebox_double, &var_double_value, + handler, miss, exit_point, on_nonexistent, support_elements); + } +} + +void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( + const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind, + TNode<WordT> handler_word, Label* rebox_double, Variable* var_double_value, + SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point, + OnNonExistent on_nonexistent, ElementSupport support_elements) { Label constant(this), field(this), normal(this, Label::kDeferred), interceptor(this, Label::kDeferred), nonexistent(this), accessor(this, Label::kDeferred), global(this, Label::kDeferred), @@ -478,7 +475,7 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( &module_export, &interceptor); BIND(&field); - HandleLoadField(holder, handler_word, &var_double_value, &rebox_double, + HandleLoadField(holder, handler_word, var_double_value, rebox_double, exit_point); BIND(&nonexistent); @@ -625,8 +622,88 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase( } } - BIND(&rebox_double); - exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value())); + BIND(rebox_double); + exit_point->Return(AllocateHeapNumberWithValue(var_double_value->value())); +} + +void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase( + const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind, + Label* miss, ExitPoint* exit_point) { + Label return_true(this), return_false(this), return_lookup(this), + normal(this), global(this); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), + &return_true); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)), + &return_true); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)), + &return_false); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)), + &normal); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)), + &return_true); + + GotoIf( + WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)), + &return_true); + + GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)), + &return_true); + + GotoIf(WordEqual(handler_kind, + IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)), + &return_true); + + Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global, + &return_lookup); + + BIND(&return_true); + exit_point->Return(TrueConstant()); + + BIND(&return_false); + exit_point->Return(FalseConstant()); + + BIND(&return_lookup); + { + CSA_ASSERT( + this, + Word32Or( + WordEqual(handler_kind, IntPtrConstant(LoadHandler::kInterceptor)), + Word32Or( + WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), + WordEqual(handler_kind, + IntPtrConstant(LoadHandler::kModuleExport))))); + exit_point->ReturnCallStub( + Builtins::CallableFor(isolate(), Builtins::kHasProperty), p->context, + p->receiver, p->name); + } + + BIND(&normal); + { + Comment("has_normal"); + TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder)); + TVARIABLE(IntPtrT, var_name_index); + Label found(this); + NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found, + &var_name_index, miss); + + BIND(&found); + exit_point->Return(TrueConstant()); + } + + BIND(&global); + { + CSA_ASSERT(this, IsPropertyCell(holder)); + // Ensure the property cell doesn't contain the hole. + Node* value = LoadObjectField(holder, PropertyCell::kValueOffset); + GotoIf(IsTheHole(value), miss); + + exit_point->Return(TrueConstant()); + } } // Performs actions common to both load and store handlers: @@ -743,7 +820,7 @@ Node* AccessorAssembler::HandleProtoHandler( void AccessorAssembler::HandleLoadICProtoHandler( const LoadICParameters* p, Node* handler, Variable* var_holder, Variable* var_smi_handler, Label* if_smi_handler, Label* miss, - ExitPoint* exit_point, ICMode ic_mode) { + ExitPoint* exit_point, ICMode ic_mode, LoadAccessMode access_mode) { DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep()); @@ -753,14 +830,18 @@ void AccessorAssembler::HandleLoadICProtoHandler( nullptr, // on_found_on_receiver [=](Node* properties, Node* name_index) { - VARIABLE(var_details, MachineRepresentation::kWord32); - VARIABLE(var_value, MachineRepresentation::kTagged); - LoadPropertyFromNameDictionary(properties, name_index, &var_details, - &var_value); - Node* value = - CallGetterIfAccessor(var_value.value(), var_details.value(), - p->context, p->receiver, miss); - exit_point->Return(value); + if (access_mode == LoadAccessMode::kHas) { + exit_point->Return(TrueConstant()); + } else { + VARIABLE(var_details, MachineRepresentation::kWord32); + VARIABLE(var_value, MachineRepresentation::kTagged); + LoadPropertyFromNameDictionary(properties, name_index, &var_details, + &var_value); + Node* value = + CallGetterIfAccessor(var_value.value(), var_details.value(), + p->context, p->receiver, miss); + exit_point->Return(value); + } }, miss, ic_mode); @@ -831,13 +912,8 @@ void AccessorAssembler::HandleStoreICNativeDataProperty( Node* accessor_info = LoadDescriptorValue(LoadMap(holder), descriptor); CSA_CHECK(this, IsAccessorInfo(accessor_info)); - // TODO(8580): Get the language mode lazily when required to avoid the - // computation of GetLanguageMode here. Also make the computation of - // language mode not dependent on vector. - Node* language_mode = GetLanguageMode(p->vector, p->slot); - TailCallRuntime(Runtime::kStoreCallbackProperty, p->context, p->receiver, - holder, accessor_info, p->name, p->value, language_mode); + holder, accessor_info, p->name, p->value); } void AccessorAssembler::HandleStoreICHandlerCase( @@ -1087,6 +1163,11 @@ void AccessorAssembler::CheckFieldType(TNode<DescriptorArray> descriptors, BIND(&all_fine); } +TNode<BoolT> AccessorAssembler::IsPropertyDetailsConst(Node* details) { + return Word32Equal(DecodeWord32<PropertyDetails::ConstnessField>(details), + Int32Constant(static_cast<int32_t>(VariableMode::kConst))); +} + void AccessorAssembler::OverwriteExistingFastDataProperty( Node* object, Node* object_map, Node* descriptors, Node* descriptor_name_index, Node* details, Node* value, Label* slow, @@ -1103,15 +1184,6 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( BIND(&if_field); { - if (FLAG_track_constant_fields && !do_transitioning_store) { - // TODO(ishell): Taking the slow path is not necessary if new and old - // values are identical. - GotoIf(Word32Equal( - DecodeWord32<PropertyDetails::ConstnessField>(details), - Int32Constant(static_cast<int32_t>(VariableMode::kConst))), - slow); - } - Node* representation = DecodeWord32<PropertyDetails::RepresentationField>(details); @@ -1141,6 +1213,13 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( if (FLAG_unbox_double_fields) { if (do_transitioning_store) { StoreMap(object, object_map); + } else if (FLAG_track_constant_fields) { + Label if_mutable(this); + GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); + Node* current_value = + LoadObjectField(object, field_offset, MachineType::Float64()); + Branch(Float64Equal(current_value, double_value), &done, slow); + BIND(&if_mutable); } StoreObjectFieldNoWriteBarrier(object, field_offset, double_value, MachineRepresentation::kFloat64); @@ -1152,6 +1231,13 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( StoreObjectField(object, field_offset, mutable_heap_number); } else { Node* mutable_heap_number = LoadObjectField(object, field_offset); + if (FLAG_track_constant_fields) { + Label if_mutable(this); + GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); + Node* current_value = LoadHeapNumberValue(mutable_heap_number); + Branch(Float64Equal(current_value, double_value), &done, slow); + BIND(&if_mutable); + } StoreHeapNumberValue(mutable_heap_number, double_value); } } @@ -1162,6 +1248,13 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( { if (do_transitioning_store) { StoreMap(object, object_map); + } else if (FLAG_track_constant_fields) { + Label if_mutable(this); + GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); + Node* current_value = + LoadObjectField(object, field_offset, MachineType::AnyTagged()); + Branch(WordEqual(current_value, value), &done, slow); + BIND(&if_mutable); } StoreObjectField(object, field_offset, value); Goto(&done); @@ -1211,11 +1304,26 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( Node* mutable_heap_number = LoadPropertyArrayElement(properties, backing_store_index); Node* double_value = ChangeNumberToFloat64(value); + if (FLAG_track_constant_fields) { + Label if_mutable(this); + GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); + Node* current_value = LoadHeapNumberValue(mutable_heap_number); + Branch(Float64Equal(current_value, double_value), &done, slow); + BIND(&if_mutable); + } StoreHeapNumberValue(mutable_heap_number, double_value); Goto(&done); } BIND(&tagged_rep); { + if (FLAG_track_constant_fields) { + Label if_mutable(this); + GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); + Node* current_value = + LoadPropertyArrayElement(properties, backing_store_index); + Branch(WordEqual(current_value, value), &done, slow); + BIND(&if_mutable); + } StorePropertyArrayElement(properties, backing_store_index, value); Goto(&done); } @@ -1422,7 +1530,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( BIND(&store); Callable callable = CodeFactory::CallApiCallback(isolate()); TNode<IntPtrT> argc = IntPtrConstant(1); - Return(CallStub(callable, nullptr, context, callback, argc, data, + Return(CallStub(callable, context, callback, argc, data, api_holder.value(), p->receiver, p->value)); } @@ -1434,17 +1542,6 @@ void AccessorAssembler::HandleStoreICProtoHandler( } } -Node* AccessorAssembler::GetLanguageMode(Node* vector, Node* slot) { - VARIABLE(var_language_mode, MachineRepresentation::kTaggedSigned, - SmiConstant(LanguageMode::kStrict)); - Label language_mode_determined(this); - BranchIfStrictMode(vector, slot, &language_mode_determined); - var_language_mode.Bind(SmiConstant(LanguageMode::kSloppy)); - Goto(&language_mode_determined); - BIND(&language_mode_determined); - return var_language_mode.value(); -} - void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p, Node* proxy, Label* miss, ElementSupport support_elements) { @@ -1454,18 +1551,13 @@ void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p, Label if_index(this), if_unique_name(this), to_name_failed(this, Label::kDeferred); - // TODO(8580): Get the language mode lazily when required to avoid the - // computation of GetLanguageMode here. Also make the computation of - // language mode not dependent on vector. - Node* language_mode = GetLanguageMode(p->vector, p->slot); - if (support_elements == kSupportElements) { TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &to_name_failed); BIND(&if_unique_name); CallBuiltin(Builtins::kProxySetProperty, p->context, proxy, - var_unique.value(), p->value, p->receiver, language_mode); + var_unique.value(), p->value, p->receiver); Return(p->value); // The index case is handled earlier by the runtime. @@ -1476,11 +1568,11 @@ void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p, BIND(&to_name_failed); TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy, - p->name, p->value, p->receiver, language_mode); + p->name, p->value, p->receiver); } else { Node* name = CallBuiltin(Builtins::kToName, p->context, p->name); TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name, - p->value, p->receiver, language_mode); + p->value, p->receiver); } } @@ -1789,7 +1881,7 @@ void AccessorAssembler::EmitElementLoad( SloppyTNode<IntPtrT> intptr_index, Node* is_jsarray_condition, Label* if_hole, Label* rebox_double, Variable* var_double_value, Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss, - ExitPoint* exit_point) { + ExitPoint* exit_point, LoadAccessMode access_mode) { Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this), if_fast_double(this), if_fast_holey_double(this), if_nonfast(this), if_dictionary(this); @@ -1821,23 +1913,31 @@ void AccessorAssembler::EmitElementLoad( BIND(&if_fast_packed); { Comment("fast packed elements"); - exit_point->Return(LoadFixedArrayElement(CAST(elements), intptr_index)); + exit_point->Return( + access_mode == LoadAccessMode::kHas + ? TrueConstant() + : UnsafeLoadFixedArrayElement(CAST(elements), intptr_index)); } BIND(&if_fast_holey); { Comment("fast holey elements"); - Node* element = LoadFixedArrayElement(CAST(elements), intptr_index); + Node* element = UnsafeLoadFixedArrayElement(CAST(elements), intptr_index); GotoIf(WordEqual(element, TheHoleConstant()), if_hole); - exit_point->Return(element); + exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant() + : element); } BIND(&if_fast_double); { Comment("packed double elements"); - var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index, - MachineType::Float64())); - Goto(rebox_double); + if (access_mode == LoadAccessMode::kHas) { + exit_point->Return(TrueConstant()); + } else { + var_double_value->Bind(LoadFixedDoubleArrayElement( + elements, intptr_index, MachineType::Float64())); + Goto(rebox_double); + } } BIND(&if_fast_holey_double); @@ -1846,8 +1946,12 @@ void AccessorAssembler::EmitElementLoad( Node* value = LoadFixedDoubleArrayElement(elements, intptr_index, MachineType::Float64(), 0, INTPTR_PARAMETERS, if_hole); - var_double_value->Bind(value); - Goto(rebox_double); + if (access_mode == LoadAccessMode::kHas) { + exit_point->Return(TrueConstant()); + } else { + var_double_value->Bind(value); + Goto(rebox_double); + } } BIND(&if_nonfast); @@ -1869,7 +1973,8 @@ void AccessorAssembler::EmitElementLoad( TNode<Object> value = BasicLoadNumberDictionaryElement( CAST(elements), intptr_index, miss, if_hole); - exit_point->Return(value); + exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant() + : value); } BIND(&if_typed_array); @@ -1882,97 +1987,101 @@ void AccessorAssembler::EmitElementLoad( // Bounds check. Node* length = SmiUntag(LoadJSTypedArrayLength(CAST(object))); GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds); - - Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements)); - - Label uint8_elements(this), int8_elements(this), uint16_elements(this), - int16_elements(this), uint32_elements(this), int32_elements(this), - float32_elements(this), float64_elements(this), bigint64_elements(this), - biguint64_elements(this); - Label* elements_kind_labels[] = { - &uint8_elements, &uint8_elements, &int8_elements, - &uint16_elements, &int16_elements, &uint32_elements, - &int32_elements, &float32_elements, &float64_elements, - &bigint64_elements, &biguint64_elements}; - int32_t elements_kinds[] = { - UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS, - UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS, - INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS, - BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS}; - const size_t kTypedElementsKindCount = - LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1; - DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds)); - DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels)); - Switch(elements_kind, miss, elements_kinds, elements_kind_labels, - kTypedElementsKindCount); - BIND(&uint8_elements); - { - Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too. - Node* element = Load(MachineType::Uint8(), backing_store, intptr_index); - exit_point->Return(SmiFromInt32(element)); - } - BIND(&int8_elements); - { - Comment("INT8_ELEMENTS"); - Node* element = Load(MachineType::Int8(), backing_store, intptr_index); - exit_point->Return(SmiFromInt32(element)); - } - BIND(&uint16_elements); - { - Comment("UINT16_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(1)); - Node* element = Load(MachineType::Uint16(), backing_store, index); - exit_point->Return(SmiFromInt32(element)); - } - BIND(&int16_elements); - { - Comment("INT16_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(1)); - Node* element = Load(MachineType::Int16(), backing_store, index); - exit_point->Return(SmiFromInt32(element)); - } - BIND(&uint32_elements); - { - Comment("UINT32_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(2)); - Node* element = Load(MachineType::Uint32(), backing_store, index); - exit_point->Return(ChangeUint32ToTagged(element)); - } - BIND(&int32_elements); - { - Comment("INT32_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(2)); - Node* element = Load(MachineType::Int32(), backing_store, index); - exit_point->Return(ChangeInt32ToTagged(element)); - } - BIND(&float32_elements); - { - Comment("FLOAT32_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(2)); - Node* element = Load(MachineType::Float32(), backing_store, index); - var_double_value->Bind(ChangeFloat32ToFloat64(element)); - Goto(rebox_double); - } - BIND(&float64_elements); - { - Comment("FLOAT64_ELEMENTS"); - Node* index = WordShl(intptr_index, IntPtrConstant(3)); - Node* element = Load(MachineType::Float64(), backing_store, index); - var_double_value->Bind(element); - Goto(rebox_double); - } - BIND(&bigint64_elements); - { - Comment("BIGINT64_ELEMENTS"); - exit_point->Return(LoadFixedTypedArrayElementAsTagged( - backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS)); - } - BIND(&biguint64_elements); - { - Comment("BIGUINT64_ELEMENTS"); - exit_point->Return(LoadFixedTypedArrayElementAsTagged( - backing_store, intptr_index, BIGUINT64_ELEMENTS, INTPTR_PARAMETERS)); + if (access_mode == LoadAccessMode::kHas) { + exit_point->Return(TrueConstant()); + } else { + Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements)); + + Label uint8_elements(this), int8_elements(this), uint16_elements(this), + int16_elements(this), uint32_elements(this), int32_elements(this), + float32_elements(this), float64_elements(this), + bigint64_elements(this), biguint64_elements(this); + Label* elements_kind_labels[] = { + &uint8_elements, &uint8_elements, &int8_elements, + &uint16_elements, &int16_elements, &uint32_elements, + &int32_elements, &float32_elements, &float64_elements, + &bigint64_elements, &biguint64_elements}; + int32_t elements_kinds[] = { + UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS, + UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS, + INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS, + BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS}; + const size_t kTypedElementsKindCount = + LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - + FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1; + DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds)); + DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels)); + Switch(elements_kind, miss, elements_kinds, elements_kind_labels, + kTypedElementsKindCount); + BIND(&uint8_elements); + { + Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too. + Node* element = Load(MachineType::Uint8(), backing_store, intptr_index); + exit_point->Return(SmiFromInt32(element)); + } + BIND(&int8_elements); + { + Comment("INT8_ELEMENTS"); + Node* element = Load(MachineType::Int8(), backing_store, intptr_index); + exit_point->Return(SmiFromInt32(element)); + } + BIND(&uint16_elements); + { + Comment("UINT16_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(1)); + Node* element = Load(MachineType::Uint16(), backing_store, index); + exit_point->Return(SmiFromInt32(element)); + } + BIND(&int16_elements); + { + Comment("INT16_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(1)); + Node* element = Load(MachineType::Int16(), backing_store, index); + exit_point->Return(SmiFromInt32(element)); + } + BIND(&uint32_elements); + { + Comment("UINT32_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(2)); + Node* element = Load(MachineType::Uint32(), backing_store, index); + exit_point->Return(ChangeUint32ToTagged(element)); + } + BIND(&int32_elements); + { + Comment("INT32_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(2)); + Node* element = Load(MachineType::Int32(), backing_store, index); + exit_point->Return(ChangeInt32ToTagged(element)); + } + BIND(&float32_elements); + { + Comment("FLOAT32_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(2)); + Node* element = Load(MachineType::Float32(), backing_store, index); + var_double_value->Bind(ChangeFloat32ToFloat64(element)); + Goto(rebox_double); + } + BIND(&float64_elements); + { + Comment("FLOAT64_ELEMENTS"); + Node* index = WordShl(intptr_index, IntPtrConstant(3)); + Node* element = Load(MachineType::Float64(), backing_store, index); + var_double_value->Bind(element); + Goto(rebox_double); + } + BIND(&bigint64_elements); + { + Comment("BIGINT64_ELEMENTS"); + exit_point->Return(LoadFixedTypedArrayElementAsTagged( + backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS)); + } + BIND(&biguint64_elements); + { + Comment("BIGUINT64_ELEMENTS"); + exit_point->Return(LoadFixedTypedArrayElementAsTagged( + backing_store, intptr_index, BIGUINT64_ELEMENTS, + INTPTR_PARAMETERS)); + } } } } @@ -1990,43 +2099,6 @@ void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, BIND(&done); } -void AccessorAssembler::BranchIfStrictMode(Node* vector, Node* slot, - Label* if_strict) { - Node* sfi = - LoadObjectField(vector, FeedbackVector::kSharedFunctionInfoOffset); - TNode<FeedbackMetadata> metadata = CAST(LoadObjectField( - sfi, SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset)); - Node* slot_int = SmiToInt32(slot); - - // See VectorICComputer::index(). - const int kItemsPerWord = FeedbackMetadata::VectorICComputer::kItemsPerWord; - Node* word_index = Int32Div(slot_int, Int32Constant(kItemsPerWord)); - Node* word_offset = Int32Mod(slot_int, Int32Constant(kItemsPerWord)); - - int32_t first_item = FeedbackMetadata::kHeaderSize - kHeapObjectTag; - Node* offset = - ElementOffsetFromIndex(ChangeInt32ToIntPtr(word_index), UINT32_ELEMENTS, - INTPTR_PARAMETERS, first_item); - - Node* data = Load(MachineType::Int32(), metadata, offset); - - // See VectorICComputer::decode(). - const int kBitsPerItem = FeedbackMetadata::kFeedbackSlotKindBits; - Node* shift = Int32Mul(word_offset, Int32Constant(kBitsPerItem)); - const int kMask = FeedbackMetadata::VectorICComputer::kMask; - Node* kind = Word32And(Word32Shr(data, shift), Int32Constant(kMask)); - - STATIC_ASSERT(FeedbackSlotKind::kStoreGlobalSloppy <= - FeedbackSlotKind::kLastSloppyKind); - STATIC_ASSERT(FeedbackSlotKind::kStoreKeyedSloppy <= - FeedbackSlotKind::kLastSloppyKind); - STATIC_ASSERT(FeedbackSlotKind::kStoreNamedSloppy <= - FeedbackSlotKind::kLastSloppyKind); - GotoIfNot(Int32LessThanOrEqual(kind, Int32Constant(static_cast<int>( - FeedbackSlotKind::kLastSloppyKind))), - if_strict); -} - void AccessorAssembler::InvalidateValidityCellIfPrototype(Node* map, Node* bitfield2) { Label is_prototype(this), cont(this); @@ -2141,10 +2213,10 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map, // for a handler in the stub cache. TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map); - Label if_descriptor_found(this), stub_cache(this); + Label if_descriptor_found(this), try_stub_cache(this); TVARIABLE(IntPtrT, var_name_index); - Label* notfound = - use_stub_cache == kUseStubCache ? &stub_cache : &lookup_prototype_chain; + Label* notfound = use_stub_cache == kUseStubCache ? &try_stub_cache + : &lookup_prototype_chain; DescriptorLookup(p->name, descriptors, bitfield3, &if_descriptor_found, &var_name_index, notfound); @@ -2157,6 +2229,13 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map, } if (use_stub_cache == kUseStubCache) { + Label stub_cache(this); + BIND(&try_stub_cache); + // When there is no feedback vector don't use stub cache. + GotoIfNot(IsUndefined(p->vector), &stub_cache); + // Fall back to the slow path for private symbols. + Branch(IsPrivateSymbol(p->name), slow, &lookup_prototype_chain); + BIND(&stub_cache); Comment("stub cache probe for fast property load"); TVARIABLE(MaybeObject, var_handler); @@ -2288,7 +2367,10 @@ Node* AccessorAssembler::StubCachePrimaryOffset(Node* name, Node* map) { // Using only the low bits in 64-bit mode is unlikely to increase the // risk of collision even if the heap is spread over an area larger than // 4Gb (and not at all if it isn't). - Node* map32 = TruncateIntPtrToInt32(BitcastTaggedToWord(map)); + Node* map_word = BitcastTaggedToWord(map); + + Node* map32 = TruncateIntPtrToInt32(UncheckedCast<IntPtrT>( + WordXor(map_word, WordShr(map_word, StubCache::kMapKeyShift)))); // Base the offset on a simple combination of name and map. Node* hash = Int32Add(hash_field, map32); uint32_t mask = (StubCache::kPrimaryTableSize - 1) @@ -2391,17 +2473,18 @@ void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p, // changes in control flow and logic. We currently have no way of ensuring // that no frame is constructed, so it's easy to break this optimization by // accident. - Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred); + Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred), + no_feedback(this, Label::kDeferred); - GotoIf(IsUndefined(p->vector), &miss); + Node* recv_map = LoadReceiverMap(p->receiver); + GotoIf(IsDeprecatedMap(recv_map), &miss); + + GotoIf(IsUndefined(p->vector), &no_feedback); // Inlined fast path. { Comment("LoadIC_BytecodeHandler_fast"); - Node* recv_map = LoadReceiverMap(p->receiver); - GotoIf(IsDeprecatedMap(recv_map), &miss); - TVARIABLE(MaybeObject, var_handler); Label try_polymorphic(this), if_handler(this, &var_handler); @@ -2418,7 +2501,7 @@ void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p, GetHeapObjectIfStrong(feedback, &miss); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &stub_call); HandlePolymorphicCase(recv_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } } @@ -2434,6 +2517,15 @@ void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p, p->receiver, p->name, p->slot, p->vector); } + BIND(&no_feedback); + { + Comment("LoadIC_BytecodeHandler_nofeedback"); + // Call into the stub that implements the non-inlined parts of LoadIC. + exit_point->ReturnCallStub( + Builtins::CallableFor(isolate(), Builtins::kLoadIC_Uninitialized), + p->context, p->receiver, p->name, p->slot, p->vector); + } + BIND(&miss); { Comment("LoadIC_BytecodeHandler_miss"); @@ -2469,7 +2561,7 @@ void AccessorAssembler::LoadIC(const LoadICParameters* p) { Comment("LoadIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&non_inlined); @@ -2518,20 +2610,61 @@ void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p, } } +// TODO(8860): This check is only required so we can make prototypes fast on +// the first load. This is not really useful when there is no feedback vector +// and may not be important when lazily allocating feedback vectors. Once lazy +// allocation of feedback vectors has landed try to eliminate this check. +void AccessorAssembler::BranchIfPrototypeShouldbeFast(Node* receiver_map, + Label* prototype_not_fast, + Label* prototype_fast) { + VARIABLE(var_map, MachineRepresentation::kTagged); + var_map.Bind(receiver_map); + Label loop_body(this, &var_map); + Goto(&loop_body); + + BIND(&loop_body); + { + Node* map = var_map.value(); + Node* prototype = LoadMapPrototype(map); + GotoIf(IsNull(prototype), prototype_fast); + TNode<PrototypeInfo> proto_info = + LoadMapPrototypeInfo(receiver_map, prototype_not_fast); + GotoIf(IsNull(prototype), prototype_not_fast); + TNode<Uint32T> flags = + LoadObjectField<Uint32T>(proto_info, PrototypeInfo::kBitFieldOffset); + GotoIf(Word32Equal(flags, Uint32Constant(0)), prototype_not_fast); + + Node* prototype_map = LoadMap(prototype); + var_map.Bind(prototype_map); + Goto(&loop_body); + } +} + void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) { - Label miss(this, Label::kDeferred); + Label miss(this, Label::kDeferred), + check_if_fast_prototype(this, Label::kDeferred), + check_function_prototype(this); Node* receiver = p->receiver; GotoIf(TaggedIsSmi(receiver), &miss); Node* receiver_map = LoadMap(receiver); Node* instance_type = LoadMapInstanceType(receiver_map); + GotoIf(IsUndefined(p->vector), &check_if_fast_prototype); // Optimistically write the state transition to the vector. StoreFeedbackVectorSlot(p->vector, p->slot, LoadRoot(RootIndex::kpremonomorphic_symbol), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); StoreWeakReferenceInFeedbackVector(p->vector, p->slot, receiver_map, kTaggedSize, SMI_PARAMETERS); + Goto(&check_function_prototype); + BIND(&check_if_fast_prototype); + { + BranchIfPrototypeShouldbeFast(receiver_map, &miss, + &check_function_prototype); + } + + BIND(&check_function_prototype); { // Special case for Function.prototype load, because it's very common // for ICs that are only executed once (MyFunc.prototype.foo = ...). @@ -2551,28 +2684,34 @@ void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) { BIND(&miss); { + Label call_runtime(this, Label::kDeferred); + GotoIf(IsUndefined(p->vector), &call_runtime); // Undo the optimistic state transition. StoreFeedbackVectorSlot(p->vector, p->slot, LoadRoot(RootIndex::kuninitialized_symbol), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); + Goto(&call_runtime); + BIND(&call_runtime); TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } } -void AccessorAssembler::LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot, +void AccessorAssembler::LoadGlobalIC(Node* vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, ParameterMode slot_mode) { Label try_handler(this, Label::kDeferred), miss(this, Label::kDeferred); - LoadGlobalIC_TryPropertyCellCase(vector, slot, lazy_context, exit_point, + GotoIf(IsUndefined(vector), &miss); + + LoadGlobalIC_TryPropertyCellCase(CAST(vector), slot, lazy_context, exit_point, &try_handler, &miss, slot_mode); BIND(&try_handler); - LoadGlobalIC_TryHandlerCase(vector, slot, lazy_context, lazy_name, + LoadGlobalIC_TryHandlerCase(CAST(vector), slot, lazy_context, lazy_name, typeof_mode, exit_point, &miss, slot_mode); BIND(&miss); @@ -2655,27 +2794,30 @@ void AccessorAssembler::LoadGlobalIC_TryHandlerCase( on_nonexistent); } -void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) { +void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p, + LoadAccessMode access_mode) { ExitPoint direct_exit(this); TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), try_polymorphic_name(this, Label::kDeferred), - miss(this, Label::kDeferred); + miss(this, Label::kDeferred), generic(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); + GotoIf(IsUndefined(p->vector), &generic); + // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { - HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit, - ICMode::kNonGlobalIC, - OnNonExistent::kReturnUndefined, kSupportElements); + HandleLoadICHandlerCase( + p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC, + OnNonExistent::kReturnUndefined, kSupportElements, access_mode); } BIND(&try_polymorphic); @@ -2685,20 +2827,26 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) { Comment("KeyedLoadIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&try_megamorphic); { // Check megamorphic case. Comment("KeyedLoadIC_try_megamorphic"); - GotoIfNot( - WordEqual(strong_feedback, LoadRoot(RootIndex::kmegamorphic_symbol)), - &try_polymorphic_name); + Branch(WordEqual(strong_feedback, LoadRoot(RootIndex::kmegamorphic_symbol)), + &generic, &try_polymorphic_name); + } + + BIND(&generic); + { // TODO(jkummerow): Inline this? Or some of it? - TailCallBuiltin(Builtins::kKeyedLoadIC_Megamorphic, p->context, p->receiver, - p->name, p->slot, p->vector); + TailCallBuiltin(access_mode == LoadAccessMode::kLoad + ? Builtins::kKeyedLoadIC_Megamorphic + : Builtins::kKeyedHasIC_Megamorphic, + p->context, p->receiver, p->name, p->slot, p->vector); } + BIND(&try_polymorphic_name); { // We might have a name in feedback, and a weak fixed array in the next @@ -2742,16 +2890,20 @@ void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) { // If the name comparison succeeded, we know we have a weak fixed array // with at least one map/handler pair. Node* name = var_name.value(); - TailCallBuiltin(Builtins::kKeyedLoadIC_PolymorphicName, p->context, - p->receiver, name, p->slot, p->vector); + TailCallBuiltin(access_mode == LoadAccessMode::kLoad + ? Builtins::kKeyedLoadIC_PolymorphicName + : Builtins::kKeyedHasIC_PolymorphicName, + p->context, p->receiver, name, p->slot, p->vector); } } BIND(&miss); { Comment("KeyedLoadIC_miss"); - TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver, - p->name, p->slot, p->vector); + TailCallRuntime(access_mode == LoadAccessMode::kLoad + ? Runtime::kKeyedLoadIC_Miss + : Runtime::kKeyedHasIC_Miss, + p->context, p->receiver, p->name, p->slot, p->vector); } } @@ -2834,7 +2986,8 @@ void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { } } -void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) { +void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p, + LoadAccessMode access_mode) { TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), miss(this, Label::kDeferred); @@ -2857,22 +3010,23 @@ void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) { TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(vector, slot, kTaggedSize, SMI_PARAMETERS); TNode<WeakFixedArray> array = CAST(feedback_element); - HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss, - 1); + HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss); BIND(&if_handler); { ExitPoint direct_exit(this); - HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit, - ICMode::kNonGlobalIC, - OnNonExistent::kReturnUndefined, kOnlyProperties); + HandleLoadICHandlerCase( + p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC, + OnNonExistent::kReturnUndefined, kOnlyProperties, access_mode); } BIND(&miss); { Comment("KeyedLoadIC_miss"); - TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, name, slot, - vector); + TailCallRuntime(access_mode == LoadAccessMode::kLoad + ? Runtime::kKeyedLoadIC_Miss + : Runtime::kKeyedHasIC_Miss, + context, receiver, name, slot, vector); } } @@ -2884,11 +3038,14 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) { if_handler_from_stub_cache(this, &var_handler, Label::kDeferred), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), - try_uninitialized(this, Label::kDeferred), miss(this, Label::kDeferred); + try_uninitialized(this, Label::kDeferred), miss(this, Label::kDeferred), + no_feedback(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); + GotoIf(IsUndefined(p->vector), &no_feedback); + // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, @@ -2907,7 +3064,7 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) { Comment("StoreIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&try_megamorphic); @@ -2923,12 +3080,17 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) { BIND(&try_uninitialized); { // Check uninitialized case. - GotoIfNot( + Branch( WordEqual(strong_feedback, LoadRoot(RootIndex::kuninitialized_symbol)), - &miss); + &no_feedback, &miss); + } + + BIND(&no_feedback); + { TailCallBuiltin(Builtins::kStoreIC_Uninitialized, p->context, p->receiver, p->name, p->value, p->slot, p->vector); } + BIND(&miss); { TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot, @@ -2937,14 +3099,18 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) { } void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) { - Label if_lexical_var(this), if_property_cell(this); + Label if_lexical_var(this), if_heapobject(this); TNode<MaybeObject> maybe_weak_ref = LoadFeedbackVectorSlot(pp->vector, pp->slot, 0, SMI_PARAMETERS); - Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell); + Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_heapobject); - BIND(&if_property_cell); + BIND(&if_heapobject); { Label try_handler(this), miss(this, Label::kDeferred); + GotoIf( + WordEqual(maybe_weak_ref, LoadRoot(RootIndex::kpremonomorphic_symbol)), + &miss); + CSA_ASSERT(this, IsWeakOrCleared(maybe_weak_ref)); TNode<PropertyCell> property_cell = CAST(GetHeapObjectAssumeWeak(maybe_weak_ref, &try_handler)); @@ -3065,11 +3231,14 @@ void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) { Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), + no_feedback(this, Label::kDeferred), try_polymorphic_name(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); + GotoIf(IsUndefined(p->vector), &no_feedback); + // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, @@ -3089,18 +3258,22 @@ void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) { GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&try_megamorphic); { // Check megamorphic case. Comment("KeyedStoreIC_try_megamorphic"); - GotoIfNot( + Branch( WordEqual(strong_feedback, LoadRoot(RootIndex::kmegamorphic_symbol)), - &try_polymorphic_name); + &no_feedback, &try_polymorphic_name); + } + + BIND(&no_feedback); + { TailCallBuiltin(Builtins::kKeyedStoreIC_Megamorphic, p->context, - p->receiver, p->name, p->value, p->slot, p->vector); + p->receiver, p->name, p->value, p->slot); } BIND(&try_polymorphic_name); @@ -3114,7 +3287,7 @@ void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) { p->vector, p->slot, kTaggedSize, SMI_PARAMETERS); TNode<WeakFixedArray> array = CAST(feedback_element); HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, - &miss, 1); + &miss); } } BIND(&miss); @@ -3136,6 +3309,9 @@ void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) { Node* array_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(array_map), &miss); + + GotoIf(IsUndefined(p->vector), &miss); + TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, array_map, &if_handler, &var_handler, &try_polymorphic); @@ -3172,7 +3348,7 @@ void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) { GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(array_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&try_megamorphic); @@ -3316,11 +3492,12 @@ void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) { Node* context = Parameter(Descriptor::kContext); ExitPoint direct_exit(this); - LoadGlobalIC(CAST(vector), slot, - // lazy_context - [=] { return CAST(context); }, - // lazy_name - [=] { return CAST(name); }, typeof_mode, &direct_exit); + LoadGlobalIC( + vector, slot, + // lazy_context + [=] { return CAST(context); }, + // lazy_name + [=] { return CAST(name); }, typeof_mode, &direct_exit); } void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) { @@ -3346,7 +3523,7 @@ void AccessorAssembler::GenerateKeyedLoadIC() { Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); - KeyedLoadIC(&p); + KeyedLoadIC(&p, LoadAccessMode::kLoad); } void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() { @@ -3398,7 +3575,7 @@ void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() { Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); - KeyedLoadICPolymorphicName(&p); + KeyedLoadICPolymorphicName(&p, LoadAccessMode::kLoad); } void AccessorAssembler::GenerateStoreGlobalIC() { @@ -3541,6 +3718,7 @@ void AccessorAssembler::GenerateCloneObjectIC_Slow() { { Label cont(this); GotoIf(IsJSObjectInstanceType(type), &cont); + GotoIf(InstanceTypeEqual(type, JS_PROXY_TYPE), &call_runtime); GotoIfNot(IsStringInstanceType(type), &done); Branch(SmiEqual(LoadStringLengthAsSmi(CAST(source)), SmiConstant(0)), &done, &call_runtime); @@ -3549,12 +3727,12 @@ void AccessorAssembler::GenerateCloneObjectIC_Slow() { GotoIfNot(IsEmptyFixedArray(LoadElements(CAST(source))), &call_runtime); - ForEachEnumerableOwnProperty(context, map, CAST(source), - [=](TNode<Name> key, TNode<Object> value) { - SetPropertyInLiteral(context, result, key, - value); - }, - &call_runtime); + ForEachEnumerableOwnProperty( + context, map, CAST(source), kPropertyAdditionOrder, + [=](TNode<Name> key, TNode<Object> value) { + SetPropertyInLiteral(context, result, key, value); + }, + &call_runtime); Goto(&done); BIND(&call_runtime); @@ -3575,10 +3753,13 @@ void AccessorAssembler::GenerateCloneObjectIC() { TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler); Label miss(this, Label::kDeferred), try_polymorphic(this, Label::kDeferred), - try_megamorphic(this, Label::kDeferred); + try_megamorphic(this, Label::kDeferred), slow(this, Label::kDeferred); TNode<Map> source_map = LoadMap(UncheckedCast<HeapObject>(source)); GotoIf(IsDeprecatedMap(source_map), &miss); + + GotoIf(IsUndefined(vector), &slow); + TNode<MaybeObject> feedback = TryMonomorphicCase( slot, vector, source_map, &if_handler, &var_handler, &try_polymorphic); @@ -3681,7 +3862,7 @@ void AccessorAssembler::GenerateCloneObjectIC() { Comment("CloneObjectIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(source_map, CAST(strong_feedback), &if_handler, - &var_handler, &miss, 2); + &var_handler, &miss); } BIND(&try_megamorphic); @@ -3695,6 +3876,11 @@ void AccessorAssembler::GenerateCloneObjectIC() { GotoIfNot( WordEqual(strong_feedback, LoadRoot(RootIndex::kmegamorphic_symbol)), &miss); + Goto(&slow); + } + + BIND(&slow); + { TailCallBuiltin(Builtins::kCloneObjectIC_Slow, context, source, flags, slot, vector); } @@ -3711,5 +3897,42 @@ void AccessorAssembler::GenerateCloneObjectIC() { } } +void AccessorAssembler::GenerateKeyedHasIC() { + typedef LoadWithVectorDescriptor Descriptor; + + Node* receiver = Parameter(Descriptor::kReceiver); + Node* name = Parameter(Descriptor::kName); + Node* slot = Parameter(Descriptor::kSlot); + Node* vector = Parameter(Descriptor::kVector); + Node* context = Parameter(Descriptor::kContext); + + LoadICParameters p(context, receiver, name, slot, vector); + KeyedLoadIC(&p, LoadAccessMode::kHas); +} + +void AccessorAssembler::GenerateKeyedHasIC_Megamorphic() { + typedef LoadWithVectorDescriptor Descriptor; + + Node* receiver = Parameter(Descriptor::kReceiver); + Node* name = Parameter(Descriptor::kName); + Node* context = Parameter(Descriptor::kContext); + // TODO(magardn): implement HasProperty handling in KeyedLoadICGeneric + Return(HasProperty(context, receiver, name, + HasPropertyLookupMode::kHasProperty)); +} + +void AccessorAssembler::GenerateKeyedHasIC_PolymorphicName() { + typedef LoadWithVectorDescriptor Descriptor; + + Node* receiver = Parameter(Descriptor::kReceiver); + Node* name = Parameter(Descriptor::kName); + Node* slot = Parameter(Descriptor::kSlot); + Node* vector = Parameter(Descriptor::kVector); + Node* context = Parameter(Descriptor::kContext); + + LoadICParameters p(context, receiver, name, slot, vector); + KeyedLoadICPolymorphicName(&p, LoadAccessMode::kHas); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/ic/accessor-assembler.h b/deps/v8/src/ic/accessor-assembler.h index 1022d0f160..beed6f655f 100644 --- a/deps/v8/src/ic/accessor-assembler.h +++ b/deps/v8/src/ic/accessor-assembler.h @@ -44,6 +44,9 @@ class AccessorAssembler : public CodeStubAssembler { void GenerateStoreGlobalICTrampoline(); void GenerateCloneObjectIC(); void GenerateCloneObjectIC_Slow(); + void GenerateKeyedHasIC(); + void GenerateKeyedHasIC_Megamorphic(); + void GenerateKeyedHasIC_PolymorphicName(); void GenerateLoadGlobalIC(TypeofMode typeof_mode); void GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode); @@ -82,7 +85,7 @@ class AccessorAssembler : public CodeStubAssembler { Node* holder; }; - void LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot, + void LoadGlobalIC(Node* vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, @@ -105,6 +108,7 @@ class AccessorAssembler : public CodeStubAssembler { SloppyTNode<Object> value; }; + enum class LoadAccessMode { kLoad, kHas }; enum class ICMode { kNonGlobalIC, kGlobalIC }; enum ElementSupport { kOnlyProperties, kSupportElements }; void HandleStoreICHandlerCase( @@ -123,8 +127,6 @@ class AccessorAssembler : public CodeStubAssembler { void JumpIfDataProperty(Node* details, Label* writable, Label* readonly); - void BranchIfStrictMode(Node* vector, Node* slot, Label* if_strict); - void InvalidateValidityCellIfPrototype(Node* map, Node* bitfield2 = nullptr); void OverwriteExistingFastDataProperty(Node* object, Node* object_map, @@ -155,9 +157,10 @@ class AccessorAssembler : public CodeStubAssembler { void LoadIC_Uninitialized(const LoadICParameters* p); - void KeyedLoadIC(const LoadICParameters* p); + void KeyedLoadIC(const LoadICParameters* p, LoadAccessMode access_mode); void KeyedLoadICGeneric(const LoadICParameters* p); - void KeyedLoadICPolymorphicName(const LoadICParameters* p); + void KeyedLoadICPolymorphicName(const LoadICParameters* p, + LoadAccessMode access_mode); void StoreIC(const StoreICParameters* p); void StoreGlobalIC(const StoreICParameters* p); void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value, @@ -175,26 +178,29 @@ class AccessorAssembler : public CodeStubAssembler { void HandlePolymorphicCase(Node* receiver_map, TNode<WeakFixedArray> feedback, Label* if_handler, TVariable<MaybeObject>* var_handler, - Label* if_miss, int min_feedback_capacity); + Label* if_miss); // LoadIC implementation. void HandleLoadICHandlerCase( const LoadICParameters* p, TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode = ICMode::kNonGlobalIC, OnNonExistent on_nonexistent = OnNonExistent::kReturnUndefined, - ElementSupport support_elements = kOnlyProperties); + ElementSupport support_elements = kOnlyProperties, + LoadAccessMode access_mode = LoadAccessMode::kLoad); void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler, SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point, OnNonExistent on_nonexistent, - ElementSupport support_elements); + ElementSupport support_elements, + LoadAccessMode access_mode); void HandleLoadICProtoHandler(const LoadICParameters* p, Node* handler, Variable* var_holder, Variable* var_smi_handler, Label* if_smi_handler, Label* miss, - ExitPoint* exit_point, ICMode ic_mode); + ExitPoint* exit_point, ICMode ic_mode, + LoadAccessMode access_mode); void HandleLoadCallbackProperty(const LoadICParameters* p, TNode<JSObject> holder, @@ -213,6 +219,18 @@ class AccessorAssembler : public CodeStubAssembler { void EmitAccessCheck(Node* expected_native_context, Node* context, Node* receiver, Label* can_access, Label* miss); + void HandleLoadICSmiHandlerLoadNamedCase( + const LoadICParameters* p, Node* holder, TNode<IntPtrT> handler_kind, + TNode<WordT> handler_word, Label* rebox_double, + Variable* var_double_value, SloppyTNode<Object> handler, Label* miss, + ExitPoint* exit_point, OnNonExistent on_nonexistent, + ElementSupport support_elements); + + void HandleLoadICSmiHandlerHasNamedCase(const LoadICParameters* p, + Node* holder, + TNode<IntPtrT> handler_kind, + Label* miss, ExitPoint* exit_point); + // LoadGlobalIC implementation. void LoadGlobalIC_TryPropertyCellCase( @@ -274,12 +292,14 @@ class AccessorAssembler : public CodeStubAssembler { const OnFoundOnReceiver& on_found_on_receiver, Label* miss, ICMode ic_mode); - Node* GetLanguageMode(Node* vector, Node* slot); - Node* PrepareValueForStore(Node* handler_word, Node* holder, Representation representation, Node* value, Label* bailout); + void BranchIfPrototypeShouldbeFast(Node* receiver_map, + Label* prototype_not_fast, + Label* prototype_fast); + // Extends properties backing store by JSObject::kFieldsAdded elements, // returns updated properties backing store. Node* ExtendPropertiesBackingStore(Node* object, Node* index); @@ -296,9 +316,11 @@ class AccessorAssembler : public CodeStubAssembler { Label* if_hole, Label* rebox_double, Variable* var_double_value, Label* unimplemented_elements_kind, Label* out_of_bounds, - Label* miss, ExitPoint* exit_point); + Label* miss, ExitPoint* exit_point, + LoadAccessMode access_mode = LoadAccessMode::kLoad); void NameDictionaryNegativeLookup(Node* object, SloppyTNode<Name> name, Label* miss); + TNode<BoolT> IsPropertyDetailsConst(Node* details); // Stub cache access helpers. diff --git a/deps/v8/src/ic/handler-configuration.h b/deps/v8/src/ic/handler-configuration.h index 1d9658e118..19ca5a9c6d 100644 --- a/deps/v8/src/ic/handler-configuration.h +++ b/deps/v8/src/ic/handler-configuration.h @@ -180,7 +180,7 @@ class LoadHandler final : public DataHandler { // Decodes the KeyedAccessLoadMode from a {handler}. static KeyedAccessLoadMode GetKeyedAccessLoadMode(MaybeObject handler); - OBJECT_CONSTRUCTORS(LoadHandler, DataHandler) + OBJECT_CONSTRUCTORS(LoadHandler, DataHandler); }; // A set of bit fields representing Smi handlers for stores and a HeapObject @@ -301,7 +301,7 @@ class StoreHandler final : public DataHandler { int descriptor, FieldIndex field_index, Representation representation); - OBJECT_CONSTRUCTORS(StoreHandler, DataHandler) + OBJECT_CONSTRUCTORS(StoreHandler, DataHandler); }; } // namespace internal diff --git a/deps/v8/src/ic/ic.cc b/deps/v8/src/ic/ic.cc index c607679a2a..b050513adb 100644 --- a/deps/v8/src/ic/ic.cc +++ b/deps/v8/src/ic/ic.cc @@ -88,7 +88,7 @@ const char* GetModifier(KeyedAccessStoreMode mode) { void IC::TraceIC(const char* type, Handle<Object> name) { if (FLAG_ic_stats) { if (AddressIsDeoptimizedCode()) return; - State new_state = nexus()->StateFromFeedback(); + State new_state = nexus()->ic_state(); TraceIC(type, name, state(), new_state); } } @@ -200,7 +200,7 @@ IC::IC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot, } pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address); DCHECK_IMPLIES(!vector.is_null(), kind_ == nexus_.kind()); - state_ = (vector.is_null()) ? NO_FEEDBACK : nexus_.StateFromFeedback(); + state_ = (vector.is_null()) ? NO_FEEDBACK : nexus_.ic_state(); old_state_ = state_; } @@ -216,7 +216,7 @@ JSFunction IC::GetHostFunction() const { return frame->function(); } -static void LookupForRead(Isolate* isolate, LookupIterator* it) { +static void LookupForRead(LookupIterator* it, bool is_has_property) { for (; it->IsFound(); it->Next()) { switch (it->state()) { case LookupIterator::NOT_FOUND: @@ -227,7 +227,13 @@ static void LookupForRead(Isolate* isolate, LookupIterator* it) { case LookupIterator::INTERCEPTOR: { // If there is a getter, return; otherwise loop to perform the lookup. Handle<JSObject> holder = it->GetHolder<JSObject>(); - if (!holder->GetNamedInterceptor()->getter()->IsUndefined(isolate)) { + if (!holder->GetNamedInterceptor()->getter()->IsUndefined( + it->isolate())) { + return; + } + if (is_has_property && + !holder->GetNamedInterceptor()->query()->IsUndefined( + it->isolate())) { return; } break; @@ -277,14 +283,13 @@ bool IC::RecomputeHandlerForName(Handle<Object> name) { if (is_keyed()) { // Determine whether the failure is due to a name failure. if (!name->IsName()) return false; - Name stub_name = nexus()->FindFirstName(); + Name stub_name = nexus()->GetName(); if (*name != stub_name) return false; } return true; } - void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) { if (state() == NO_FEEDBACK) return; update_receiver_map(receiver); @@ -306,7 +311,6 @@ MaybeHandle<Object> IC::TypeError(MessageTemplate index, Handle<Object> object, THROW_NEW_ERROR(isolate(), NewTypeError(index, key, object), Object); } - MaybeHandle<Object> IC::ReferenceError(Handle<Name> name) { HandleScope scope(isolate()); THROW_NEW_ERROR( @@ -429,7 +433,8 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) { // If the object is undefined or null it's illegal to try to get any // of its properties; throw a TypeError in that case. - if (object->IsNullOrUndefined(isolate())) { + if (IsAnyHas() ? !object->IsJSReceiver() + : object->IsNullOrUndefined(isolate())) { if (use_ic && state() != PREMONOMORPHIC) { // Ensure the IC state progresses. TRACE_HANDLER_STATS(isolate(), LoadIC_NonReceiver); @@ -441,7 +446,9 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) { if (*name == ReadOnlyRoots(isolate()).iterator_symbol()) { return Runtime::ThrowIteratorError(isolate(), object); } - return TypeError(MessageTemplate::kNonObjectPropertyLoad, object, name); + return TypeError(IsAnyHas() ? MessageTemplate::kInvalidInOperatorUse + : MessageTemplate::kNonObjectPropertyLoad, + object, name); } if (MigrateDeprecated(object)) use_ic = false; @@ -450,9 +457,11 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) { JSObject::MakePrototypesFast(object, kStartAtReceiver, isolate()); update_receiver_map(object); } - // Named lookup in the object. + LookupIterator it(isolate(), object, name); - LookupForRead(isolate(), &it); + + // Named lookup in the object. + LookupForRead(&it, IsAnyHas()); if (name->IsPrivate()) { if (name->IsPrivateName() && !it.IsFound()) { @@ -473,6 +482,14 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) { // Update inline cache and stub cache. if (use_ic) UpdateCaches(&it); + if (IsAnyHas()) { + // Named lookup in the object. + Maybe<bool> maybe = JSReceiver::HasProperty(&it); + if (maybe.IsNothing()) return MaybeHandle<Object>(); + return maybe.FromJust() ? ReadOnlyRoots(isolate()).true_value_handle() + : ReadOnlyRoots(isolate()).false_value_handle(); + } + // Get the property. Handle<Object> result; @@ -498,7 +515,7 @@ MaybeHandle<Object> LoadGlobalIC::Load(Handle<Name> name) { global->native_context()->script_context_table(), isolate()); ScriptContextTable::LookupResult lookup_result; - if (ScriptContextTable::Lookup(isolate(), script_contexts, str_name, + if (ScriptContextTable::Lookup(isolate(), *script_contexts, *str_name, &lookup_result)) { Handle<Context> script_context = ScriptContextTable::GetContext( isolate(), script_contexts, lookup_result.context_index); @@ -514,8 +531,9 @@ MaybeHandle<Object> LoadGlobalIC::Load(Handle<Name> name) { bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic; if (use_ic) { - if (nexus()->ConfigureLexicalVarMode(lookup_result.context_index, - lookup_result.slot_index)) { + if (nexus()->ConfigureLexicalVarMode( + lookup_result.context_index, lookup_result.slot_index, + lookup_result.mode == VariableMode::kConst)) { TRACE_HANDLER_STATS(isolate(), LoadGlobalIC_LoadScriptContextField); } else { // Given combination of indices can't be encoded, so use slow stub. @@ -546,7 +564,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, const MaybeObjectHandle& handler) { DCHECK(IsHandler(*handler)); if (is_keyed() && state() != RECOMPUTE_HANDLER) { - if (nexus()->FindFirstName() != *name) return false; + if (nexus()->GetName() != *name) return false; } Handle<Map> map = receiver_map(); MapHandles maps; @@ -586,7 +604,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, int number_of_valid_maps = number_of_maps - deprecated_maps - (handler_to_overwrite != -1); - if (number_of_valid_maps >= kMaxPolymorphicMapCount) return false; + if (number_of_valid_maps >= FLAG_max_polymorphic_map_count) return false; if (number_of_maps == 0 && state() != MONOMORPHIC && state() != POLYMORPHIC) { return false; } @@ -595,7 +613,7 @@ bool IC::UpdatePolymorphicIC(Handle<Name> name, if (number_of_valid_maps == 1) { ConfigureVectorState(name, receiver_map(), handler); } else { - if (is_keyed() && nexus()->FindFirstName() != *name) return false; + if (is_keyed() && nexus()->GetName() != *name) return false; if (handler_to_overwrite >= 0) { handlers[handler_to_overwrite] = handler; if (!map.is_identical_to(maps.at(handler_to_overwrite))) { @@ -618,7 +636,6 @@ void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler, ConfigureVectorState(name, receiver_map(), handler); } - void IC::CopyICToMegamorphicCache(Handle<Name> name) { MapHandles maps; MaybeObjectHandles handlers; @@ -653,7 +670,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> handler) { void IC::PatchCache(Handle<Name> name, const MaybeObjectHandle& handler) { DCHECK(IsHandler(*handler)); // Currently only load and store ICs support non-code handlers. - DCHECK(IsAnyLoad() || IsAnyStore()); + DCHECK(IsAnyLoad() || IsAnyStore() || IsAnyHas()); switch (state()) { case NO_FEEDBACK: break; @@ -727,6 +744,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) { } StubCache* IC::stub_cache() { + DCHECK(!IsAnyHas()); if (IsAnyLoad()) { return isolate()->load_stub_cache(); } else { @@ -737,13 +755,15 @@ StubCache* IC::stub_cache() { void IC::UpdateMegamorphicCache(Handle<Map> map, Handle<Name> name, const MaybeObjectHandle& handler) { - stub_cache()->Set(*name, *map, *handler); + if (!IsAnyHas()) { + stub_cache()->Set(*name, *map, *handler); + } } void IC::TraceHandlerCacheHitStats(LookupIterator* lookup) { DCHECK_EQ(LookupIterator::ACCESSOR, lookup->state()); if (V8_LIKELY(!FLAG_runtime_stats)) return; - if (IsAnyLoad()) { + if (IsAnyLoad() || IsAnyHas()) { TRACE_HANDLER_STATS(isolate(), LoadIC_HandlerCacheHit_Accessor); } else { DCHECK(IsAnyStore()); @@ -754,22 +774,29 @@ void IC::TraceHandlerCacheHitStats(LookupIterator* lookup) { Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) { Handle<Object> receiver = lookup->GetReceiver(); ReadOnlyRoots roots(isolate()); - if (receiver->IsString() && *lookup->name() == roots.length_string()) { - TRACE_HANDLER_STATS(isolate(), LoadIC_StringLength); - return BUILTIN_CODE(isolate(), LoadIC_StringLength); - } - if (receiver->IsStringWrapper() && *lookup->name() == roots.length_string()) { - TRACE_HANDLER_STATS(isolate(), LoadIC_StringWrapperLength); - return BUILTIN_CODE(isolate(), LoadIC_StringWrapperLength); - } + // `in` cannot be called on strings, and will always return true for string + // wrapper length and function prototypes. The latter two cases are given + // LoadHandler::LoadNativeDataProperty below. + if (!IsAnyHas()) { + if (receiver->IsString() && *lookup->name() == roots.length_string()) { + TRACE_HANDLER_STATS(isolate(), LoadIC_StringLength); + return BUILTIN_CODE(isolate(), LoadIC_StringLength); + } + + if (receiver->IsStringWrapper() && + *lookup->name() == roots.length_string()) { + TRACE_HANDLER_STATS(isolate(), LoadIC_StringWrapperLength); + return BUILTIN_CODE(isolate(), LoadIC_StringWrapperLength); + } - // Use specialized code for getting prototype of functions. - if (receiver->IsJSFunction() && *lookup->name() == roots.prototype_string() && - !JSFunction::cast(*receiver)->PrototypeRequiresRuntimeLookup()) { - Handle<Code> stub; - TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub); - return BUILTIN_CODE(isolate(), LoadIC_FunctionPrototype); + // Use specialized code for getting prototype of functions. + if (receiver->IsJSFunction() && + *lookup->name() == roots.prototype_string() && + !JSFunction::cast(*receiver)->PrototypeRequiresRuntimeLookup()) { + TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub); + return BUILTIN_CODE(isolate(), LoadIC_FunctionPrototype); + } } Handle<Map> map = receiver_map(); @@ -1081,24 +1108,66 @@ void KeyedLoadIC::UpdateLoadElement(Handle<HeapObject> receiver, } } +namespace { + +bool AllowConvertHoleElementToUndefined(Isolate* isolate, + Handle<Map> receiver_map) { + if (receiver_map->IsJSTypedArrayMap()) { + // For JSTypedArray we never lookup elements in the prototype chain. + return true; + } + + // For other {receiver}s we need to check the "no elements" protector. + if (isolate->IsNoElementsProtectorIntact()) { + if (receiver_map->IsStringMap()) { + return true; + } + if (receiver_map->IsJSObjectMap()) { + // For other JSObjects (including JSArrays) we can only continue if + // the {receiver}s prototype is either the initial Object.prototype + // or the initial Array.prototype, which are both guarded by the + // "no elements" protector checked above. + Handle<Object> receiver_prototype(receiver_map->prototype(), isolate); + + if (isolate->IsInAnyContext(*receiver_prototype, + Context::INITIAL_ARRAY_PROTOTYPE_INDEX) || + isolate->IsInAnyContext(*receiver_prototype, + Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) { + return true; + } + } + } + + return false; +} +} // namespace + Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map, KeyedAccessLoadMode load_mode) { + // Has a getter interceptor, or is any has and has a query interceptor. if (receiver_map->has_indexed_interceptor() && - !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined( - isolate()) && + (!receiver_map->GetIndexedInterceptor()->getter()->IsUndefined( + isolate()) || + (IsAnyHas() && + !receiver_map->GetIndexedInterceptor()->query()->IsUndefined( + isolate()))) && !receiver_map->GetIndexedInterceptor()->non_masking()) { // TODO(jgruber): Update counter name. TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedInterceptorStub); - return BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC); + return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIndexedInterceptorIC) + : BUILTIN_CODE(isolate(), LoadIndexedInterceptorIC); } + InstanceType instance_type = receiver_map->instance_type(); if (instance_type < FIRST_NONSTRING_TYPE) { TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedStringDH); + if (IsAnyHas()) return BUILTIN_CODE(isolate(), HasIC_Slow); return LoadHandler::LoadIndexedString(isolate(), load_mode); } if (instance_type < FIRST_JS_RECEIVER_TYPE) { TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub); - return BUILTIN_CODE(isolate(), KeyedLoadIC_Slow); + return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIC_Slow) + : BUILTIN_CODE(isolate(), KeyedLoadIC_Slow); } if (instance_type == JS_PROXY_TYPE) { return LoadHandler::LoadProxy(isolate()); @@ -1108,7 +1177,8 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map, if (IsSloppyArgumentsElementsKind(elements_kind)) { // TODO(jgruber): Update counter name. TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_KeyedLoadSloppyArgumentsStub); - return BUILTIN_CODE(isolate(), KeyedLoadIC_SloppyArguments); + return IsAnyHas() ? BUILTIN_CODE(isolate(), KeyedHasIC_SloppyArguments) + : BUILTIN_CODE(isolate(), KeyedLoadIC_SloppyArguments); } bool is_js_array = instance_type == JS_ARRAY_TYPE; if (elements_kind == DICTIONARY_ELEMENTS) { @@ -1118,11 +1188,10 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map, } DCHECK(IsFastElementsKind(elements_kind) || IsFixedTypedArrayElementsKind(elements_kind)); - // TODO(jkummerow): Use IsHoleyOrDictionaryElementsKind(elements_kind). bool convert_hole_to_undefined = - is_js_array && elements_kind == HOLEY_ELEMENTS && - *receiver_map == - isolate()->raw_native_context()->GetInitialJSArrayMap(elements_kind); + (elements_kind == HOLEY_SMI_ELEMENTS || + elements_kind == HOLEY_ELEMENTS) && + AllowConvertHoleElementToUndefined(isolate(), receiver_map); TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadElementDH); return LoadHandler::LoadElement(isolate(), elements_kind, convert_hole_to_undefined, is_js_array, @@ -1198,46 +1267,39 @@ bool IsOutOfBoundsAccess(Handle<Object> receiver, uint32_t index) { KeyedAccessLoadMode GetLoadMode(Isolate* isolate, Handle<Object> receiver, uint32_t index) { if (IsOutOfBoundsAccess(receiver, index)) { - if (receiver->IsJSTypedArray()) { - // For JSTypedArray we never lookup elements in the prototype chain. + DCHECK(receiver->IsHeapObject()); + Handle<Map> receiver_map(Handle<HeapObject>::cast(receiver)->map(), + isolate); + if (AllowConvertHoleElementToUndefined(isolate, receiver_map)) { return LOAD_IGNORE_OUT_OF_BOUNDS; } - - // For other {receiver}s we need to check the "no elements" protector. - if (isolate->IsNoElementsProtectorIntact()) { - if (receiver->IsString()) { - // ToObject(receiver) will have the initial String.prototype. - return LOAD_IGNORE_OUT_OF_BOUNDS; - } - if (receiver->IsJSObject()) { - // For other JSObjects (including JSArrays) we can only continue if - // the {receiver}s prototype is either the initial Object.prototype - // or the initial Array.prototype, which are both guarded by the - // "no elements" protector checked above. - Handle<Object> receiver_prototype( - JSObject::cast(*receiver)->map()->prototype(), isolate); - if (isolate->IsInAnyContext(*receiver_prototype, - Context::INITIAL_ARRAY_PROTOTYPE_INDEX) || - isolate->IsInAnyContext(*receiver_prototype, - Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) { - return LOAD_IGNORE_OUT_OF_BOUNDS; - } - } - } } return STANDARD_LOAD; } } // namespace -MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object, - Handle<Object> key) { - if (MigrateDeprecated(object)) { - Handle<Object> result; +MaybeHandle<Object> KeyedLoadIC::RuntimeLoad(Handle<Object> object, + Handle<Object> key) { + Handle<Object> result; + + if (IsKeyedLoadIC()) { ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, Runtime::GetObjectProperty(isolate(), object, key), Object); - return result; + } else { + DCHECK(IsKeyedHasIC()); + ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, + Runtime::HasProperty(isolate(), object, key), + Object); + } + return result; +} + +MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object, + Handle<Object> key) { + if (MigrateDeprecated(object)) { + return RuntimeLoad(object, key); } Handle<Object> load_handle; @@ -1268,11 +1330,7 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object, if (!load_handle.is_null()) return load_handle; - Handle<Object> result; - ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, - Runtime::GetObjectProperty(isolate(), object, key), - Object); - return result; + return RuntimeLoad(object, key); } bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value, @@ -1357,7 +1415,7 @@ MaybeHandle<Object> StoreGlobalIC::Store(Handle<Name> name, global->native_context()->script_context_table(), isolate()); ScriptContextTable::LookupResult lookup_result; - if (ScriptContextTable::Lookup(isolate(), script_contexts, str_name, + if (ScriptContextTable::Lookup(isolate(), *script_contexts, *str_name, &lookup_result)) { Handle<Context> script_context = ScriptContextTable::GetContext( isolate(), script_contexts, lookup_result.context_index); @@ -1376,8 +1434,9 @@ MaybeHandle<Object> StoreGlobalIC::Store(Handle<Name> name, bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic; if (use_ic) { - if (nexus()->ConfigureLexicalVarMode(lookup_result.context_index, - lookup_result.slot_index)) { + if (nexus()->ConfigureLexicalVarMode( + lookup_result.context_index, lookup_result.slot_index, + lookup_result.mode == VariableMode::kConst)) { TRACE_HANDLER_STATS(isolate(), StoreGlobalIC_StoreScriptContextField); } else { // Given combination of indices can't be encoded, so use slow stub. @@ -1402,8 +1461,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name, if (MigrateDeprecated(object)) { Handle<Object> result; ASSIGN_RETURN_ON_EXCEPTION( - isolate(), result, - Object::SetProperty(isolate(), object, name, value, language_mode()), + isolate(), result, Object::SetProperty(isolate(), object, name, value), Object); return result; } @@ -1443,8 +1501,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name, } if (use_ic) UpdateCaches(&it, value, store_origin); - MAYBE_RETURN_NULL( - Object::SetProperty(&it, value, language_mode(), store_origin)); + MAYBE_RETURN_NULL(Object::SetProperty(&it, value, store_origin)); return value; } @@ -1473,6 +1530,24 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value, } handler = ComputeHandler(lookup); } else { + if (state() == UNINITIALIZED && IsStoreGlobalIC() && + lookup->state() == LookupIterator::INTERCEPTOR) { + InterceptorInfo info = + lookup->GetHolder<JSObject>()->GetNamedInterceptor(); + if (!lookup->HolderIsReceiverOrHiddenPrototype() && + !info->getter()->IsUndefined(isolate())) { + // Utilize premonomorphic state for global store ics that run into + // an interceptor because the property doesn't exist yet. + // After we actually set the property, we'll have more information. + // Premonomorphism gives us a chance to find more information the + // second time. + TRACE_HANDLER_STATS(isolate(), StoreGlobalIC_Premonomorphic); + ConfigureVectorState(receiver_map()); + TraceIC("StoreGlobalIC", lookup->name()); + return; + } + } + set_slow_stub_reason("LookupForWrite said 'false'"); // TODO(marja): change slow_stub to return MaybeObjectHandle. handler = MaybeObjectHandle(slow_stub()); @@ -1814,7 +1889,6 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map, } } - Handle<Map> KeyedStoreIC::ComputeTransitionedMap( Handle<Map> map, KeyedAccessStoreMode store_mode) { switch (store_mode) { @@ -1952,7 +2026,6 @@ void KeyedStoreIC::StoreElementPolymorphicHandlers( } } - static KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, uint32_t index, Handle<Object> value) { bool oob_access = IsOutOfBoundsAccess(receiver, index); @@ -1997,7 +2070,6 @@ static KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, } } - MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object, Handle<Object> key, Handle<Object> value) { @@ -2008,7 +2080,7 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object, ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, Runtime::SetObjectProperty(isolate(), object, key, value, - language_mode(), StoreOrigin::kMaybeKeyed), + StoreOrigin::kMaybeKeyed), Object); return result; } @@ -2083,7 +2155,7 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object, Handle<JSArray>::cast(object)->elements()->IsCowArray(); ASSIGN_RETURN_ON_EXCEPTION( isolate(), store_handle, - Runtime::SetObjectProperty(isolate(), object, key, value, language_mode(), + Runtime::SetObjectProperty(isolate(), object, key, value, StoreOrigin::kMaybeKeyed), Object); @@ -2129,8 +2201,8 @@ void StoreOwnElement(Isolate* isolate, Handle<JSArray> array, isolate, array, index, &success, LookupIterator::OWN); DCHECK(success); - CHECK(JSObject::DefineOwnPropertyIgnoreAttributes(&it, value, NONE, - kThrowOnError) + CHECK(JSObject::DefineOwnPropertyIgnoreAttributes( + &it, value, NONE, Just(ShouldThrow::kThrowOnError)) .FromJust()); } } // namespace @@ -2176,20 +2248,6 @@ void StoreInArrayLiteralIC::Store(Handle<JSArray> array, Handle<Object> index, // Static IC stub generators. // // -namespace { - -// TODO(8580): Compute the language mode lazily to avoid the expensive -// computation of language mode here. -LanguageMode GetLanguageMode(Handle<FeedbackVector> vector, Context context) { - LanguageMode language_mode = vector->shared_function_info()->language_mode(); - if (context->scope_info()->language_mode() > language_mode) { - return context->scope_info()->language_mode(); - } - return language_mode; -} - -} // namespace - RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { HandleScope scope(isolate); DCHECK_EQ(4, args.length()); @@ -2274,7 +2332,7 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { native_context->script_context_table(), isolate); ScriptContextTable::LookupResult lookup_result; - if (ScriptContextTable::Lookup(isolate, script_contexts, name, + if (ScriptContextTable::Lookup(isolate, *script_contexts, *name, &lookup_result)) { Handle<Context> script_context = ScriptContextTable::GetContext( isolate, script_contexts, lookup_result.context_index); @@ -2334,51 +2392,27 @@ RUNTIME_FUNCTION(Runtime_StoreIC_Miss) { // Runtime functions don't follow the IC's calling convention. Handle<Object> value = args.at(0); Handle<Smi> slot = args.at<Smi>(1); - Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); + Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); Handle<Object> receiver = args.at(3); Handle<Name> key = args.at<Name>(4); FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); - FeedbackSlotKind kind = vector->GetKind(vector_slot); - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); - if (IsStoreICKind(kind) || IsStoreOwnICKind(kind)) { - StoreIC ic(isolate, vector, vector_slot, kind, language_mode); - ic.UpdateState(receiver, key); - RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); - } else if (IsStoreGlobalICKind(kind)) { - DCHECK_EQ(isolate->native_context()->global_proxy(), *receiver); - receiver = isolate->global_object(); - StoreGlobalIC ic(isolate, vector, vector_slot, kind, language_mode); - ic.UpdateState(receiver, key); - RETURN_RESULT_OR_FAILURE(isolate, ic.Store(key, value)); - } else { - DCHECK(IsKeyedStoreICKind(kind)); - KeyedStoreIC ic(isolate, vector, vector_slot, kind, language_mode); - ic.UpdateState(receiver, key); - RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); + + // When there is no feedback vector it is OK to use the StoreNamedStrict as + // the feedback slot kind. We only need if it is StoreOwnICKind when + // installing the handler for storing const properties. This will happen only + // when feedback vector is available. + FeedbackSlotKind kind = FeedbackSlotKind::kStoreNamedStrict; + Handle<FeedbackVector> vector = Handle<FeedbackVector>(); + if (!maybe_vector->IsUndefined()) { + DCHECK(maybe_vector->IsFeedbackVector()); + vector = Handle<FeedbackVector>::cast(maybe_vector); + kind = vector->GetKind(vector_slot); } -} -RUNTIME_FUNCTION(Runtime_StoreICNoFeedback_Miss) { - HandleScope scope(isolate); - DCHECK_EQ(5, args.length()); - Handle<Object> value = args.at(0); - Handle<Object> receiver = args.at(1); - Handle<Name> key = args.at<Name>(2); - CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 3); - CONVERT_INT32_ARG_CHECKED(is_own_property_value, 4); - NamedPropertyType property_type = - static_cast<NamedPropertyType>(is_own_property_value); - - FeedbackSlotKind kind = (language_mode == LanguageMode::kStrict) - ? FeedbackSlotKind::kStoreNamedStrict - : FeedbackSlotKind::kStoreNamedSloppy; - if (property_type == NamedPropertyType::kOwn) { - language_mode = LanguageMode::kStrict; - kind = FeedbackSlotKind::kStoreOwnNamed; - } - StoreIC ic(isolate, Handle<FeedbackVector>(), FeedbackSlot(), kind, - language_mode); + DCHECK(IsStoreICKind(kind) || IsStoreOwnICKind(kind)); + StoreIC ic(isolate, vector, vector_slot, kind); + ic.UpdateState(receiver, key); RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); } @@ -2393,8 +2427,7 @@ RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Miss) { FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); FeedbackSlotKind kind = vector->GetKind(vector_slot); - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); - StoreGlobalIC ic(isolate, vector, vector_slot, kind, language_mode); + StoreGlobalIC ic(isolate, vector, vector_slot, kind); Handle<JSGlobalObject> global = isolate->global_object(); ic.UpdateState(global, key); RETURN_RESULT_OR_FAILURE(isolate, ic.Store(key, value)); @@ -2402,31 +2435,30 @@ RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Miss) { RUNTIME_FUNCTION(Runtime_StoreGlobalICNoFeedback_Miss) { HandleScope scope(isolate); - DCHECK_EQ(3, args.length()); + DCHECK_EQ(2, args.length()); // Runtime functions don't follow the IC's calling convention. Handle<Object> value = args.at(0); Handle<Name> key = args.at<Name>(1); - CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 2); - FeedbackSlotKind kind = (language_mode == LanguageMode::kStrict) - ? FeedbackSlotKind::kStoreGlobalStrict - : FeedbackSlotKind::kStoreGlobalSloppy; - StoreGlobalIC ic(isolate, Handle<FeedbackVector>(), FeedbackSlot(), kind, - language_mode); + // TODO(mythria): Replace StoreGlobalStrict/Sloppy with StoreNamed. + StoreGlobalIC ic(isolate, Handle<FeedbackVector>(), FeedbackSlot(), + FeedbackSlotKind::kStoreGlobalStrict); RETURN_RESULT_OR_FAILURE(isolate, ic.Store(key, value)); } +// TODO(mythria): Remove Feedback vector and slot. Since they are not used apart +// from the DCHECK. RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Slow) { HandleScope scope(isolate); DCHECK_EQ(5, args.length()); // Runtime functions don't follow the IC's calling convention. Handle<Object> value = args.at(0); - Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); CONVERT_ARG_HANDLE_CHECKED(String, name, 4); #ifdef DEBUG { Handle<Smi> slot = args.at<Smi>(1); + Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); FeedbackSlotKind slot_kind = vector->GetKind(vector_slot); DCHECK(IsStoreGlobalICKind(slot_kind)); @@ -2441,7 +2473,7 @@ RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Slow) { native_context->script_context_table(), isolate); ScriptContextTable::LookupResult lookup_result; - if (ScriptContextTable::Lookup(isolate, script_contexts, name, + if (ScriptContextTable::Lookup(isolate, *script_contexts, *name, &lookup_result)) { Handle<Context> script_context = ScriptContextTable::GetContext( isolate, script_contexts, lookup_result.context_index); @@ -2462,11 +2494,9 @@ RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Slow) { return *value; } - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); RETURN_RESULT_OR_FAILURE( - isolate, - Runtime::SetObjectProperty(isolate, global, name, value, language_mode, - StoreOrigin::kMaybeKeyed)); + isolate, Runtime::SetObjectProperty(isolate, global, name, value, + StoreOrigin::kMaybeKeyed)); } RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) { @@ -2475,18 +2505,29 @@ RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) { // Runtime functions don't follow the IC's calling convention. Handle<Object> value = args.at(0); Handle<Smi> slot = args.at<Smi>(1); - Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); + Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); Handle<Object> receiver = args.at(3); Handle<Object> key = args.at(4); - - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); - FeedbackSlotKind kind = vector->GetKind(vector_slot); + + // When the feedback vector is not valid the slot can only be of type + // StoreKeyed. Storing in array literals falls back to + // StoreInArrayLiterIC_Miss. This function is also used from store handlers + // installed in feedback vectors. In such cases, we need to get the kind from + // feedback vector slot since the handlers are used for both for StoreKeyed + // and StoreInArrayLiteral kinds. + FeedbackSlotKind kind = FeedbackSlotKind::kStoreKeyedStrict; + Handle<FeedbackVector> vector = Handle<FeedbackVector>(); + if (!maybe_vector->IsUndefined()) { + DCHECK(maybe_vector->IsFeedbackVector()); + vector = Handle<FeedbackVector>::cast(maybe_vector); + kind = vector->GetKind(vector_slot); + } // The elements store stubs miss into this function, but they are shared by // different ICs. if (IsKeyedStoreICKind(kind)) { - KeyedStoreIC ic(isolate, vector, vector_slot, kind, language_mode); + KeyedStoreIC ic(isolate, vector, vector_slot, kind); ic.UpdateState(receiver, key); RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); } else { @@ -2500,23 +2541,6 @@ RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) { } } -RUNTIME_FUNCTION(Runtime_KeyedStoreICNoFeedback_Miss) { - HandleScope scope(isolate); - DCHECK_EQ(4, args.length()); - // Runtime functions don't follow the IC's calling convention. - Handle<Object> value = args.at(0); - Handle<Object> receiver = args.at(1); - Handle<Object> key = args.at(2); - CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 3); - - FeedbackSlotKind kind = (language_mode == LanguageMode::kStrict) - ? FeedbackSlotKind::kStoreKeyedStrict - : FeedbackSlotKind::kStoreKeyedSloppy; - KeyedStoreIC ic(isolate, Handle<FeedbackVector>(), FeedbackSlot(), kind, - language_mode); - RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); -} - RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Miss) { HandleScope scope(isolate); DCHECK_EQ(5, args.length()); @@ -2541,17 +2565,14 @@ RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Miss) { RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Slow) { HandleScope scope(isolate); - DCHECK_EQ(5, args.length()); + DCHECK_EQ(3, args.length()); // Runtime functions don't follow the IC's calling convention. Handle<Object> value = args.at(0); - Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); - Handle<Object> object = args.at(3); - Handle<Object> key = args.at(4); - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); + Handle<Object> object = args.at(1); + Handle<Object> key = args.at(2); RETURN_RESULT_OR_FAILURE( - isolate, - Runtime::SetObjectProperty(isolate, object, key, value, language_mode, - StoreOrigin::kMaybeKeyed)); + isolate, Runtime::SetObjectProperty(isolate, object, key, value, + StoreOrigin::kMaybeKeyed)); } RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Slow) { @@ -2588,11 +2609,9 @@ RUNTIME_FUNCTION(Runtime_ElementsTransitionAndStoreIC_Miss) { return *value; } else { DCHECK(IsKeyedStoreICKind(kind) || IsStoreICKind(kind)); - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); RETURN_RESULT_OR_FAILURE( - isolate, - Runtime::SetObjectProperty(isolate, object, key, value, language_mode, - StoreOrigin::kMaybeKeyed)); + isolate, Runtime::SetObjectProperty(isolate, object, key, value, + StoreOrigin::kMaybeKeyed)); } } @@ -2734,22 +2753,18 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) { Handle<AccessorInfo> info = args.at<AccessorInfo>(2); Handle<Name> name = args.at<Name>(3); Handle<Object> value = args.at(4); - CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 5); HandleScope scope(isolate); if (V8_UNLIKELY(FLAG_runtime_stats)) { RETURN_RESULT_OR_FAILURE( - isolate, - Runtime::SetObjectProperty(isolate, receiver, name, value, - language_mode, StoreOrigin::kMaybeKeyed)); + isolate, Runtime::SetObjectProperty(isolate, receiver, name, value, + StoreOrigin::kMaybeKeyed)); } DCHECK(info->IsCompatibleReceiver(*receiver)); - ShouldThrow should_throw = - is_sloppy(language_mode) ? kDontThrow : kThrowOnError; PropertyCallbackArguments arguments(isolate, info->data(), *receiver, *holder, - should_throw); + Nothing<ShouldThrow>()); arguments.CallAccessorSetter(info, name, value); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); return *value; @@ -2765,7 +2780,7 @@ RUNTIME_FUNCTION(Runtime_LoadCallbackProperty) { DCHECK(info->IsCompatibleReceiver(*receiver)); PropertyCallbackArguments custom_args(isolate, info->data(), *receiver, - *holder, kThrowOnError); + *holder, Just(kThrowOnError)); Handle<Object> result = custom_args.CallAccessorGetter(info, name); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); if (result.is_null()) return ReadOnlyRoots(isolate).undefined_value(); @@ -2813,7 +2828,7 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate); PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, - *holder, kDontThrow); + *holder, Just(kDontThrow)); Handle<Object> result = arguments.CallNamedGetter(interceptor, name); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); @@ -2848,7 +2863,6 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { isolate, NewReferenceError(MessageTemplate::kNotDefined, it.name())); } - RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { HandleScope scope(isolate); DCHECK_EQ(5, args.length()); @@ -2859,7 +2873,6 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { Handle<JSObject> receiver = args.at<JSObject>(3); Handle<Name> name = args.at<Name>(4); FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); - LanguageMode language_mode = GetLanguageMode(vector, isolate->context()); // TODO(ishell): Cache interceptor_holder in the store handler like we do // for LoadHandler::kInterceptor case. @@ -2876,7 +2889,7 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { DCHECK(!interceptor->non_masking()); PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, - *receiver, kDontThrow); + *receiver, Just(kDontThrow)); Handle<Object> result = arguments.CallNamedSetter(interceptor, name, value); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); @@ -2892,13 +2905,11 @@ RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); it.Next(); - MAYBE_RETURN( - Object::SetProperty(&it, value, language_mode, StoreOrigin::kNamed), - ReadOnlyRoots(isolate).exception()); + MAYBE_RETURN(Object::SetProperty(&it, value, StoreOrigin::kNamed), + ReadOnlyRoots(isolate).exception()); return *value; } - RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { // TODO(verwaest): This should probably get the holder and receiver as input. HandleScope scope(isolate); @@ -2909,7 +2920,7 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(), isolate); PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, - *receiver, kDontThrow); + *receiver, Just(kDontThrow)); Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); @@ -2924,5 +2935,61 @@ RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { return *result; } + +RUNTIME_FUNCTION(Runtime_KeyedHasIC_Miss) { + HandleScope scope(isolate); + DCHECK_EQ(4, args.length()); + // Runtime functions don't follow the IC's calling convention. + Handle<Object> receiver = args.at(0); + Handle<Object> key = args.at(1); + Handle<Smi> slot = args.at<Smi>(2); + Handle<HeapObject> maybe_vector = args.at<HeapObject>(3); + + Handle<FeedbackVector> vector = Handle<FeedbackVector>(); + if (!maybe_vector->IsUndefined()) { + DCHECK(maybe_vector->IsFeedbackVector()); + vector = Handle<FeedbackVector>::cast(maybe_vector); + } + FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); + KeyedLoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kHasKeyed); + ic.UpdateState(receiver, key); + RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); +} + +RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) { + HandleScope scope(isolate); + Handle<JSObject> receiver = args.at<JSObject>(0); + DCHECK_GE(args.smi_at(1), 0); + uint32_t index = args.smi_at(1); + + Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(), + isolate); + PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, + *receiver, Just(kDontThrow)); + + if (!interceptor->query()->IsUndefined(isolate)) { + Handle<Object> result = arguments.CallIndexedQuery(interceptor, index); + if (!result.is_null()) { + int32_t value; + CHECK(result->ToInt32(&value)); + return value == ABSENT ? ReadOnlyRoots(isolate).false_value() + : ReadOnlyRoots(isolate).true_value(); + } + } else if (!interceptor->getter()->IsUndefined(isolate)) { + Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); + if (!result.is_null()) { + return ReadOnlyRoots(isolate).true_value(); + } + } + + LookupIterator it(isolate, receiver, index, receiver); + DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); + it.Next(); + Maybe<bool> maybe = JSReceiver::HasProperty(&it); + if (maybe.IsNothing()) return ReadOnlyRoots(isolate).exception(); + return maybe.FromJust() ? ReadOnlyRoots(isolate).true_value() + : ReadOnlyRoots(isolate).false_value(); +} + } // namespace internal } // namespace v8 diff --git a/deps/v8/src/ic/ic.h b/deps/v8/src/ic/ic.h index 9ed469410f..aa6ccd9c76 100644 --- a/deps/v8/src/ic/ic.h +++ b/deps/v8/src/ic/ic.h @@ -31,10 +31,6 @@ class IC { static constexpr int kMaxKeyedPolymorphism = 4; - // A polymorphic IC can handle at most 4 distinct maps before transitioning - // to megamorphic state. - static constexpr int kMaxPolymorphicMapCount = 4; - // Construct the IC structure with the given number of extra // JavaScript frames on the stack. IC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot, @@ -54,6 +50,7 @@ class IC { state_ = RECOMPUTE_HANDLER; } + bool IsAnyHas() const { return IsKeyedHasIC(); } bool IsAnyLoad() const { return IsLoadIC() || IsLoadGlobalIC() || IsKeyedLoadIC(); } @@ -134,9 +131,10 @@ class IC { bool IsStoreIC() const { return IsStoreICKind(kind_); } bool IsStoreOwnIC() const { return IsStoreOwnICKind(kind_); } bool IsKeyedStoreIC() const { return IsKeyedStoreICKind(kind_); } + bool IsKeyedHasIC() const { return IsKeyedHasICKind(kind_); } bool is_keyed() const { return IsKeyedLoadIC() || IsKeyedStoreIC() || - IsStoreInArrayLiteralICKind(kind_); + IsStoreInArrayLiteralICKind(kind_) || IsKeyedHasIC(); } bool ShouldRecomputeHandler(Handle<String> name); @@ -204,13 +202,12 @@ class IC { DISALLOW_IMPLICIT_CONSTRUCTORS(IC); }; - class LoadIC : public IC { public: LoadIC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot, FeedbackSlotKind kind) : IC(isolate, vector, slot, kind) { - DCHECK(IsAnyLoad()); + DCHECK(IsAnyLoad() || IsAnyHas()); } static bool ShouldThrowReferenceError(FeedbackSlotKind kind) { @@ -226,7 +223,8 @@ class LoadIC : public IC { protected: virtual Handle<Code> slow_stub() const { - return BUILTIN_CODE(isolate(), LoadIC_Slow); + return IsAnyHas() ? BUILTIN_CODE(isolate(), HasIC_Slow) + : BUILTIN_CODE(isolate(), LoadIC_Slow); } // Update the inline cache and the global stub cache based on the @@ -264,6 +262,9 @@ class KeyedLoadIC : public LoadIC { Handle<Object> key); protected: + V8_WARN_UNUSED_RESULT MaybeHandle<Object> RuntimeLoad(Handle<Object> object, + Handle<Object> key); + // receiver is HeapObject because it could be a String or a JSObject void UpdateLoadElement(Handle<HeapObject> receiver, KeyedAccessLoadMode load_mode); @@ -284,17 +285,14 @@ class KeyedLoadIC : public LoadIC { bool CanChangeToAllowOutOfBounds(Handle<Map> receiver_map); }; - class StoreIC : public IC { public: StoreIC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot, - FeedbackSlotKind kind, LanguageMode language_mode) - : IC(isolate, vector, slot, kind), language_mode_(language_mode) { + FeedbackSlotKind kind) + : IC(isolate, vector, slot, kind) { DCHECK(IsAnyStore()); } - LanguageMode language_mode() const { return language_mode_; } - V8_WARN_UNUSED_RESULT MaybeHandle<Object> Store( Handle<Object> object, Handle<Name> name, Handle<Object> value, StoreOrigin store_origin = StoreOrigin::kNamed); @@ -314,11 +312,6 @@ class StoreIC : public IC { void UpdateCaches(LookupIterator* lookup, Handle<Object> value, StoreOrigin store_origin); - // TODO(v8:8580): Instead of storing the language mode, compute it lazily - // from the closure and context when needed. We only need it when throwing - // exceptions, so it is OK to be slow. - LanguageMode language_mode_; - private: MaybeObjectHandle ComputeHandler(LookupIterator* lookup); @@ -328,9 +321,8 @@ class StoreIC : public IC { class StoreGlobalIC : public StoreIC { public: StoreGlobalIC(Isolate* isolate, Handle<FeedbackVector> vector, - FeedbackSlot slot, FeedbackSlotKind kind, - LanguageMode language_mode) - : StoreIC(isolate, vector, slot, kind, language_mode) {} + FeedbackSlot slot, FeedbackSlotKind kind) + : StoreIC(isolate, vector, slot, kind) {} V8_WARN_UNUSED_RESULT MaybeHandle<Object> Store(Handle<Name> name, Handle<Object> value); @@ -343,10 +335,8 @@ class StoreGlobalIC : public StoreIC { enum KeyedStoreCheckMap { kDontCheckMap, kCheckMap }; - enum KeyedStoreIncrementLength { kDontIncrementLength, kIncrementLength }; - class KeyedStoreIC : public StoreIC { public: KeyedAccessStoreMode GetKeyedAccessStoreMode() { @@ -354,9 +344,8 @@ class KeyedStoreIC : public StoreIC { } KeyedStoreIC(Isolate* isolate, Handle<FeedbackVector> vector, - FeedbackSlot slot, FeedbackSlotKind kind, - LanguageMode language_mode) - : StoreIC(isolate, vector, slot, kind, language_mode) {} + FeedbackSlot slot, FeedbackSlotKind kind) + : StoreIC(isolate, vector, slot, kind) {} V8_WARN_UNUSED_RESULT MaybeHandle<Object> Store(Handle<Object> object, Handle<Object> name, @@ -390,8 +379,7 @@ class StoreInArrayLiteralIC : public KeyedStoreIC { StoreInArrayLiteralIC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot) : KeyedStoreIC(isolate, vector, slot, - FeedbackSlotKind::kStoreInArrayLiteral, - LanguageMode::kStrict) { + FeedbackSlotKind::kStoreInArrayLiteral) { DCHECK(IsStoreInArrayLiteralICKind(kind())); } diff --git a/deps/v8/src/ic/keyed-store-generic.cc b/deps/v8/src/ic/keyed-store-generic.cc index 2a8bd37130..650007ab1d 100644 --- a/deps/v8/src/ic/keyed-store-generic.cc +++ b/deps/v8/src/ic/keyed-store-generic.cc @@ -60,8 +60,7 @@ class KeyedStoreGenericAssembler : public AccessorAssembler { // Helper that is used by the public KeyedStoreGeneric and by SetProperty. void KeyedStoreGeneric(TNode<Context> context, TNode<Object> receiver, TNode<Object> key, TNode<Object> value, - Maybe<LanguageMode> language_mode, TNode<Smi> slot, - TNode<FeedbackVector> vector); + Maybe<LanguageMode> language_mode); void EmitGenericElementStore(Node* receiver, Node* receiver_map, Node* instance_type, Node* intptr_index, @@ -912,29 +911,21 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore( BIND(¬_callable); { - bool handle_strict = true; - Label strict(this); LanguageMode language_mode; if (maybe_language_mode.To(&language_mode)) { if (language_mode == LanguageMode::kStrict) { - Goto(&strict); - } else { - handle_strict = false; - exit_point->Return(p->value); - } - } else { - BranchIfStrictMode(p->vector, p->slot, &strict); - exit_point->Return(p->value); - } - - if (handle_strict) { - BIND(&strict); - { exit_point->ReturnCallRuntime( Runtime::kThrowTypeError, p->context, SmiConstant(MessageTemplate::kNoSetterInCallback), p->name, var_accessor_holder.value()); + } else { + exit_point->Return(p->value); } + } else { + CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context, + SmiConstant(MessageTemplate::kNoSetterInCallback), + p->name, var_accessor_holder.value()); + exit_point->Return(p->value); } } } @@ -943,28 +934,21 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore( if (!ShouldReconfigureExisting()) { BIND(&readonly); { - bool handle_strict = true; - Label strict(this); LanguageMode language_mode; if (maybe_language_mode.To(&language_mode)) { if (language_mode == LanguageMode::kStrict) { - Goto(&strict); + Node* type = Typeof(p->receiver); + ThrowTypeError(p->context, MessageTemplate::kStrictReadOnlyProperty, + p->name, type, p->receiver); } else { - handle_strict = false; exit_point->Return(p->value); } } else { - BranchIfStrictMode(p->vector, p->slot, &strict); + CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context, + SmiConstant(MessageTemplate::kStrictReadOnlyProperty), + p->name, Typeof(p->receiver), p->receiver); exit_point->Return(p->value); } - if (handle_strict) { - BIND(&strict); - { - Node* type = Typeof(p->receiver); - ThrowTypeError(p->context, MessageTemplate::kStrictReadOnlyProperty, - p->name, type, p->receiver); - } - } } } } @@ -972,8 +956,7 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore( // Helper that is used by the public KeyedStoreGeneric and by SetProperty. void KeyedStoreGenericAssembler::KeyedStoreGeneric( TNode<Context> context, TNode<Object> receiver, TNode<Object> key, - TNode<Object> value, Maybe<LanguageMode> language_mode, TNode<Smi> slot, - TNode<FeedbackVector> vector) { + TNode<Object> value, Maybe<LanguageMode> language_mode) { TVARIABLE(IntPtrT, var_index); TVARIABLE(Object, var_unique, key); Label if_index(this), if_unique_name(this), not_internalized(this), @@ -999,8 +982,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric( BIND(&if_unique_name); { Comment("key is unique name"); - StoreICParameters p(context, receiver, var_unique.value(), value, slot, - vector); + StoreICParameters p(context, receiver, var_unique.value(), value, nullptr, + nullptr); ExitPoint direct_exit(this); EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &direct_exit, &slow, language_mode); @@ -1020,19 +1003,8 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric( { if (IsKeyedStore()) { Comment("KeyedStoreGeneric_slow"); - if (language_mode.IsJust()) { - TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key, - value, SmiConstant(language_mode.FromJust())); - } else { - TVARIABLE(Smi, var_language_mode, SmiConstant(LanguageMode::kStrict)); - Label call_runtime(this); - BranchIfStrictMode(vector, slot, &call_runtime); - var_language_mode = SmiConstant(LanguageMode::kSloppy); - Goto(&call_runtime); - BIND(&call_runtime); - TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key, - value, var_language_mode.value()); - } + TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key, + value); } else { DCHECK(IsStoreInLiteral()); TailCallRuntime(Runtime::kStoreDataPropertyInLiteral, context, receiver, @@ -1042,17 +1014,14 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric( } void KeyedStoreGenericAssembler::KeyedStoreGeneric() { - typedef StoreWithVectorDescriptor Descriptor; + typedef StoreDescriptor Descriptor; TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver)); TNode<Object> name = CAST(Parameter(Descriptor::kName)); TNode<Object> value = CAST(Parameter(Descriptor::kValue)); - TNode<Smi> slot = CAST(Parameter(Descriptor::kSlot)); - TNode<FeedbackVector> vector = CAST(Parameter(Descriptor::kVector)); TNode<Context> context = CAST(Parameter(Descriptor::kContext)); - KeyedStoreGeneric(context, receiver, name, value, Nothing<LanguageMode>(), - slot, vector); + KeyedStoreGeneric(context, receiver, name, value, Nothing<LanguageMode>()); } void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context, @@ -1060,8 +1029,7 @@ void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context, TNode<Object> key, TNode<Object> value, LanguageMode language_mode) { - KeyedStoreGeneric(context, receiver, key, value, Just(language_mode), - TNode<Smi>(), TNode<FeedbackVector>()); + KeyedStoreGeneric(context, receiver, key, value, Just(language_mode)); } void KeyedStoreGenericAssembler::StoreIC_Uninitialized() { @@ -1074,7 +1042,7 @@ void KeyedStoreGenericAssembler::StoreIC_Uninitialized() { Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); - Label miss(this); + Label miss(this, Label::kDeferred), store_property(this); GotoIf(TaggedIsSmi(receiver), &miss); Node* receiver_map = LoadMap(receiver); @@ -1084,19 +1052,29 @@ void KeyedStoreGenericAssembler::StoreIC_Uninitialized() { GotoIf(IsSpecialReceiverInstanceType(instance_type), &miss); // Optimistically write the state transition to the vector. + GotoIf(IsUndefined(vector), &store_property); StoreFeedbackVectorSlot(vector, slot, LoadRoot(RootIndex::kpremonomorphic_symbol), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); + Goto(&store_property); - StoreICParameters p(context, receiver, name, value, slot, vector); - EmitGenericPropertyStore(receiver, receiver_map, &p, &miss); + BIND(&store_property); + { + StoreICParameters p(context, receiver, name, value, slot, vector); + EmitGenericPropertyStore(receiver, receiver_map, &p, &miss); + } BIND(&miss); { + Label call_runtime(this); // Undo the optimistic state transition. + GotoIf(IsUndefined(vector), &call_runtime); StoreFeedbackVectorSlot(vector, slot, LoadRoot(RootIndex::kuninitialized_symbol), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); + Goto(&call_runtime); + + BIND(&call_runtime); TailCallRuntime(Runtime::kStoreIC_Miss, context, value, slot, vector, receiver, name); } @@ -1127,7 +1105,7 @@ void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context, unique_name, value); } else { CallRuntime(Runtime::kSetKeyedProperty, context, receiver, unique_name, - value, SmiConstant(language_mode)); + value); } Goto(&done); } diff --git a/deps/v8/src/ic/stub-cache.cc b/deps/v8/src/ic/stub-cache.cc index 8567799f3f..89a34ef80c 100644 --- a/deps/v8/src/ic/stub-cache.cc +++ b/deps/v8/src/ic/stub-cache.cc @@ -7,7 +7,7 @@ #include "src/ast/ast.h" #include "src/base/bits.h" #include "src/counters.h" -#include "src/heap/heap.h" +#include "src/heap/heap-inl.h" // For InYoungGeneration(). #include "src/ic/ic-inl.h" namespace v8 { @@ -36,7 +36,8 @@ int StubCache::PrimaryOffset(Name name, Map map) { // Using only the low bits in 64-bit mode is unlikely to increase the // risk of collision even if the heap is spread over an area larger than // 4Gb (and not at all if it isn't). - uint32_t map_low32bits = static_cast<uint32_t>(map.ptr()); + uint32_t map_low32bits = + static_cast<uint32_t>(map.ptr() ^ (map.ptr() >> kMapKeyShift)); // Base the offset on a simple combination of name and map. uint32_t key = map_low32bits + field; return key & ((kPrimaryTableSize - 1) << kCacheIndexShift); @@ -67,8 +68,8 @@ bool CommonStubCacheChecks(StubCache* stub_cache, Name name, Map map, MaybeObject handler) { // Validate that the name and handler do not move on scavenge, and that we // can use identity checks instead of structural equality checks. - DCHECK(!Heap::InNewSpace(name)); - DCHECK(!Heap::InNewSpace(handler)); + DCHECK(!Heap::InYoungGeneration(name)); + DCHECK(!Heap::InYoungGeneration(handler)); DCHECK(name->IsUniqueName()); DCHECK(name->HasHashCode()); if (handler->ptr() != kNullAddress) DCHECK(IC::IsHandler(handler)); diff --git a/deps/v8/src/ic/stub-cache.h b/deps/v8/src/ic/stub-cache.h index 0b6f9d43d1..7eaa31bd95 100644 --- a/deps/v8/src/ic/stub-cache.h +++ b/deps/v8/src/ic/stub-cache.h @@ -89,6 +89,10 @@ class StubCache { static const int kSecondaryTableBits = 9; static const int kSecondaryTableSize = (1 << kSecondaryTableBits); + // We compute the hash code for a map as follows: + // <code> = <address> ^ (<address> >> kMapKeyShift) + static const int kMapKeyShift = kPrimaryTableBits + kCacheIndexShift; + // Some magic number used in the secondary hash computation. static const int kSecondaryMagic = 0xb16ca6e5; |