From e086746ed7f95d883535060dc771ce4f3a9bd38b Mon Sep 17 00:00:00 2001 From: "verwaest@chromium.org" Date: Wed, 23 Jan 2013 15:35:43 +0000 Subject: [PATCH] Move polymorphic stub computation and compilation to stub cache Review URL: https://chromiumcodereview.appspot.com/11953025 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13478 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ic.cc | 437 ++++++++++++++++++++++-------------------------------- src/ic.h | 276 +++++++++++----------------------- src/stub-cache.cc | 204 ++++++++++++++++++------- src/stub-cache.h | 20 ++- 4 files changed, 432 insertions(+), 505 deletions(-) diff --git a/src/ic.cc b/src/ic.cc index b90436a..b759878 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -830,9 +830,9 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, } -MaybeObject* IC::Load(State state, - Handle object, - Handle name) { +MaybeObject* LoadIC::Load(State state, + Handle object, + Handle 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->IsUndefined() || object->IsNull()) { @@ -1086,42 +1086,6 @@ void LoadIC::UpdateLoadCaches(LookupResult* lookup, } -Handle KeyedLoadIC::GetElementStubWithoutMapCheck( - bool is_js_array, - ElementsKind elements_kind, - KeyedAccessGrowMode grow_mode) { - ASSERT(grow_mode == DO_NOT_ALLOW_JSARRAY_GROWTH); - if (IsFastElementsKind(elements_kind) || - IsExternalArrayElementsKind(elements_kind)) { - return KeyedLoadFastElementStub(is_js_array, elements_kind).GetCode(); - } else { - ASSERT(elements_kind == DICTIONARY_ELEMENTS); - return KeyedLoadDictionaryElementStub().GetCode(); - } -} - - -Handle KeyedLoadIC::ComputePolymorphicStub( - MapHandleList* receiver_maps, - StrictModeFlag strict_mode, - KeyedAccessGrowMode growth_mode) { - CodeHandleList handler_ics(receiver_maps->length()); - for (int i = 0; i < receiver_maps->length(); ++i) { - Handle receiver_map = receiver_maps->at(i); - Handle cached_stub = ComputeMonomorphicStubWithoutMapCheck( - receiver_map, strict_mode, growth_mode); - handler_ics.Add(cached_stub); - } - KeyedLoadStubCompiler compiler(isolate()); - Handle code = compiler.CompileLoadPolymorphic( - receiver_maps, &handler_ics); - isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); - PROFILE(isolate(), - CodeCreateEvent(Logger::KEYED_LOAD_POLYMORPHIC_IC_TAG, *code, 0)); - return code; -} - - static Handle TryConvertKey(Handle key, Isolate* isolate) { // This helper implements a few common fast cases for converting // non-smi keys of keyed loads/stores to a smi or a string. @@ -1142,6 +1106,115 @@ static Handle TryConvertKey(Handle key, Isolate* isolate) { } +static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, + Handle new_receiver_map) { + ASSERT(!new_receiver_map.is_null()); + for (int current = 0; current < receiver_maps->length(); ++current) { + if (!receiver_maps->at(current).is_null() && + receiver_maps->at(current).is_identical_to(new_receiver_map)) { + return false; + } + } + receiver_maps->Add(new_receiver_map); + return true; +} + + +static void GetReceiverMapsForStub(Handle stub, + MapHandleList* result) { + ASSERT(stub->is_inline_cache_stub()); + if (stub->is_keyed_load_stub() || stub->is_keyed_store_stub()) { + switch (stub->ic_state()) { + case MONOMORPHIC: + result->Add(Handle(stub->FindFirstMap())); + break; + case POLYMORPHIC: { + AssertNoAllocation no_allocation; + int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); + for (RelocIterator it(*stub, mask); !it.done(); it.next()) { + RelocInfo* info = it.rinfo(); + Handle object(info->target_object()); + ASSERT(object->IsMap()); + AddOneReceiverMapIfMissing(result, Handle::cast(object)); + } + break; + } + case MEGAMORPHIC: + case GENERIC: + break; + case UNINITIALIZED: + case PREMONOMORPHIC: + case MONOMORPHIC_PROTOTYPE_FAILURE: + case DEBUG_STUB: + UNREACHABLE(); + break; + } + } +} + + +Handle KeyedLoadIC::LoadElementStub(Handle receiver) { + State ic_state = target()->ic_state(); + + // Don't handle megamorphic property accesses for INTERCEPTORS or CALLBACKS + // via megamorphic stubs, since they don't have a map in their relocation info + // and so the stubs can't be harvested for the object needed for a map check. + if (target()->type() != Code::NORMAL) { + TRACE_GENERIC_IC("KeyedIC", "non-NORMAL target type"); + return generic_stub(); + } + + Handle receiver_map(receiver->map()); + MapHandleList target_receiver_maps; + if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) { + // Optimistically assume that ICs that haven't reached the MONOMORPHIC state + // yet will do so and stay there. + return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map); + } + + if (target() == *string_stub()) { + target_receiver_maps.Add(isolate()->factory()->string_map()); + } else { + GetReceiverMapsForStub(Handle(target()), &target_receiver_maps); + } + + // The first time a receiver is seen that is a transitioned version of the + // previous monomorphic receiver type, assume the new ElementsKind is the + // monomorphic type. This benefits global arrays that only transition + // once, and all call sites accessing them are faster if they remain + // monomorphic. If this optimistic assumption is not true, the IC will + // miss again and it will become polymorphic and support both the + // untransitioned and transitioned maps. + if (ic_state == MONOMORPHIC && + IsMoreGeneralElementsKindTransition( + target_receiver_maps.at(0)->elements_kind(), + receiver->GetElementsKind())) { + return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map); + } + + ASSERT(target() != *generic_stub()); + + // Determine the list of receiver maps that this call site has seen, + // adding the map that was just encountered. + if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map)) { + // If the miss wasn't due to an unseen map, a polymorphic stub + // won't help, use the generic stub. + TRACE_GENERIC_IC("KeyedIC", "same map added twice"); + return generic_stub(); + } + + // If the maximum number of receiver maps has been exceeded, use the generic + // version of the IC. + if (target_receiver_maps.length() > kMaxKeyedPolymorphism) { + TRACE_GENERIC_IC("KeyedIC", "max polymorph exceeded"); + return generic_stub(); + } + + return isolate()->stub_cache()->ComputeLoadElementPolymorphic( + &target_receiver_maps); +} + + MaybeObject* KeyedLoadIC::Load(State state, Handle object, Handle key, @@ -1151,7 +1224,7 @@ MaybeObject* KeyedLoadIC::Load(State state, key = TryConvertKey(key, isolate()); if (key->IsSymbol()) { - return IC::Load(state, object, Handle::cast(key)); + return LoadIC::Load(state, object, Handle::cast(key)); } // Do not use ICs for objects that require access checks (including @@ -1173,7 +1246,7 @@ MaybeObject* KeyedLoadIC::Load(State state, } else if (receiver->HasIndexedInterceptor()) { stub = indexed_interceptor_stub(); } else if (key->IsSmi() && (target() != *non_strict_arguments_stub())) { - stub = ComputeStub(receiver, KeyedIC::LOAD, kNonStrictMode, stub); + stub = LoadElementStub(receiver); } } } else { @@ -1311,12 +1384,12 @@ static bool LookupForWrite(Handle receiver, } -MaybeObject* IC::Store(State state, - StrictModeFlag strict_mode, - Handle object, - Handle name, - Handle value, - JSReceiver::StoreFromKeyed store_mode) { +MaybeObject* StoreIC::Store(State state, + StrictModeFlag strict_mode, + Handle object, + Handle name, + Handle value, + JSReceiver::StoreFromKeyed store_mode) { // Handle proxies. if (object->IsJSProxy()) { return JSProxy::cast(*object)-> @@ -1537,59 +1610,9 @@ void StoreIC::UpdateStoreCaches(LookupResult* lookup, } -static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps, - Handle new_receiver_map) { - ASSERT(!new_receiver_map.is_null()); - for (int current = 0; current < receiver_maps->length(); ++current) { - if (!receiver_maps->at(current).is_null() && - receiver_maps->at(current).is_identical_to(new_receiver_map)) { - return false; - } - } - receiver_maps->Add(new_receiver_map); - return true; -} - - -void KeyedIC::GetReceiverMapsForStub(Handle stub, - MapHandleList* result) { - ASSERT(stub->is_inline_cache_stub()); - if (!string_stub().is_null() && stub.is_identical_to(string_stub())) { - return result->Add(isolate()->factory()->string_map()); - } else if (stub->is_keyed_load_stub() || stub->is_keyed_store_stub()) { - switch (stub->ic_state()) { - case MONOMORPHIC: - result->Add(Handle(stub->FindFirstMap())); - break; - case POLYMORPHIC: { - AssertNoAllocation no_allocation; - int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); - for (RelocIterator it(*stub, mask); !it.done(); it.next()) { - RelocInfo* info = it.rinfo(); - Handle object(info->target_object()); - ASSERT(object->IsMap()); - AddOneReceiverMapIfMissing(result, Handle::cast(object)); - } - break; - } - case MEGAMORPHIC: - case GENERIC: - break; - case UNINITIALIZED: - case PREMONOMORPHIC: - case MONOMORPHIC_PROTOTYPE_FAILURE: - case DEBUG_STUB: - UNREACHABLE(); - break; - } - } -} - - -Handle KeyedIC::ComputeStub(Handle receiver, - StubKind stub_kind, - StrictModeFlag strict_mode, - Handle generic_stub) { +Handle KeyedStoreIC::StoreElementStub(Handle receiver, + StubKind stub_kind, + StrictModeFlag strict_mode) { State ic_state = target()->ic_state(); KeyedAccessGrowMode grow_mode = IsGrowStubKind(stub_kind) ? ALLOW_JSARRAY_GROWTH @@ -1600,65 +1623,61 @@ Handle KeyedIC::ComputeStub(Handle receiver, // and so the stubs can't be harvested for the object needed for a map check. if (target()->type() != Code::NORMAL) { TRACE_GENERIC_IC("KeyedIC", "non-NORMAL target type"); - return generic_stub; + return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub(); } - bool monomorphic = false; - bool is_transition_stub = IsTransitionStubKind(stub_kind); Handle receiver_map(receiver->map()); - Handle monomorphic_map = receiver_map; MapHandleList target_receiver_maps; if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) { // Optimistically assume that ICs that haven't reached the MONOMORPHIC state // yet will do so and stay there. - monomorphic = true; - } else { - GetReceiverMapsForStub(Handle(target()), &target_receiver_maps); - if (ic_state == MONOMORPHIC && (is_transition_stub || stub_kind == LOAD)) { - // The first time a receiver is seen that is a transitioned version of the - // previous monomorphic receiver type, assume the new ElementsKind is the - // monomorphic type. This benefits global arrays that only transition - // once, and all call sites accessing them are faster if they remain - // monomorphic. If this optimistic assumption is not true, the IC will - // miss again and it will become polymorphic and support both the - // untransitioned and transitioned maps. - monomorphic = IsMoreGeneralElementsKindTransition( + stub_kind = GetNoTransitionStubKind(stub_kind); + return isolate()->stub_cache()->ComputeKeyedStoreElement( + receiver_map, stub_kind, strict_mode, grow_mode); + } + + GetReceiverMapsForStub(Handle(target()), &target_receiver_maps); + // The first time a receiver is seen that is a transitioned version of the + // previous monomorphic receiver type, assume the new ElementsKind is the + // monomorphic type. This benefits global arrays that only transition + // once, and all call sites accessing them are faster if they remain + // monomorphic. If this optimistic assumption is not true, the IC will + // miss again and it will become polymorphic and support both the + // untransitioned and transitioned maps. + if (ic_state == MONOMORPHIC && + IsTransitionStubKind(stub_kind) && + IsMoreGeneralElementsKindTransition( target_receiver_maps.at(0)->elements_kind(), - receiver->GetElementsKind()); - } + receiver->GetElementsKind())) { + Handle monomorphic_map = ComputeTransitionedMap(receiver, stub_kind); + ASSERT(*monomorphic_map != *receiver_map); + stub_kind = GetNoTransitionStubKind(stub_kind); + return isolate()->stub_cache()->ComputeKeyedStoreElement( + monomorphic_map, stub_kind, strict_mode, grow_mode); } - if (monomorphic) { - if (is_transition_stub) { - monomorphic_map = ComputeTransitionedMap(receiver, stub_kind); - ASSERT(*monomorphic_map != *receiver_map); - stub_kind = GetNoTransitionStubKind(stub_kind); - } - return ComputeMonomorphicStub( - monomorphic_map, stub_kind, strict_mode, generic_stub); - } - ASSERT(target() != *generic_stub); + ASSERT(target() != *generic_stub() && target() != *generic_stub_strict()); - // Determine the list of receiver maps that this call site has seen, - // adding the map that was just encountered. bool map_added = AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map); + if (IsTransitionStubKind(stub_kind)) { Handle new_map = ComputeTransitionedMap(receiver, stub_kind); map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps, new_map); } + if (!map_added) { // If the miss wasn't due to an unseen map, a polymorphic stub // won't help, use the generic stub. TRACE_GENERIC_IC("KeyedIC", "same map added twice"); - return generic_stub; + return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub(); } // If the maximum number of receiver maps has been exceeded, use the generic // version of the IC. if (target_receiver_maps.length() > kMaxKeyedPolymorphism) { TRACE_GENERIC_IC("KeyedIC", "max polymorph exceeded"); - return generic_stub; + return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub(); } if ((Code::GetKeyedAccessGrowMode(target()->extra_ic_state()) == @@ -1666,81 +1685,34 @@ Handle KeyedIC::ComputeStub(Handle receiver, grow_mode = ALLOW_JSARRAY_GROWTH; } - Handle cache = - isolate()->factory()->polymorphic_code_cache(); - Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, - strict_mode); - Code::Flags flags = Code::ComputeFlags(kind(), POLYMORPHIC, extra_state); - Handle probe = cache->Lookup(&target_receiver_maps, flags); - if (probe->IsCode()) return Handle::cast(probe); - - Handle stub = - ComputePolymorphicStub(&target_receiver_maps, strict_mode, grow_mode); - PolymorphicCodeCache::Update(cache, &target_receiver_maps, flags, stub); - return stub; -} - - -Handle KeyedIC::ComputeMonomorphicStubWithoutMapCheck( - Handle receiver_map, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode) { - if ((receiver_map->instance_type() & kNotStringTag) == 0) { - ASSERT(!string_stub().is_null()); - return string_stub(); - } else { - ASSERT(receiver_map->has_dictionary_elements() || - receiver_map->has_fast_smi_or_object_elements() || - receiver_map->has_fast_double_elements() || - receiver_map->has_external_array_elements()); - bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; - return GetElementStubWithoutMapCheck(is_js_array, - receiver_map->elements_kind(), - grow_mode); - } -} - - -Handle KeyedIC::ComputeMonomorphicStub(Handle receiver_map, - StubKind stub_kind, - StrictModeFlag strict_mode, - Handle generic_stub) { - ElementsKind elements_kind = receiver_map->elements_kind(); - if (IsFastElementsKind(elements_kind) || - IsExternalArrayElementsKind(elements_kind) || - IsDictionaryElementsKind(elements_kind)) { - return isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement( - receiver_map, stub_kind, strict_mode); - } else { - return generic_stub; - } + return isolate()->stub_cache()->ComputeStoreElementPolymorphic( + &target_receiver_maps, grow_mode, strict_mode); } -Handle KeyedIC::ComputeTransitionedMap(Handle receiver, - StubKind stub_kind) { +Handle KeyedStoreIC::ComputeTransitionedMap(Handle receiver, + StubKind stub_kind) { switch (stub_kind) { - case KeyedIC::STORE_TRANSITION_SMI_TO_OBJECT: - case KeyedIC::STORE_TRANSITION_DOUBLE_TO_OBJECT: - case KeyedIC::STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT: - case KeyedIC::STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT: + case STORE_TRANSITION_SMI_TO_OBJECT: + case STORE_TRANSITION_DOUBLE_TO_OBJECT: + case STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT: + case STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT: return JSObject::GetElementsTransitionMap(receiver, FAST_ELEMENTS); - case KeyedIC::STORE_TRANSITION_SMI_TO_DOUBLE: - case KeyedIC::STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE: + case STORE_TRANSITION_SMI_TO_DOUBLE: + case STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE: return JSObject::GetElementsTransitionMap(receiver, FAST_DOUBLE_ELEMENTS); - case KeyedIC::STORE_TRANSITION_HOLEY_SMI_TO_OBJECT: - case KeyedIC::STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: - case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT: - case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: + case STORE_TRANSITION_HOLEY_SMI_TO_OBJECT: + case STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: + case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT: + case STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT: return JSObject::GetElementsTransitionMap(receiver, FAST_HOLEY_ELEMENTS); - case KeyedIC::STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE: - case KeyedIC::STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE: + case STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE: + case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE: return JSObject::GetElementsTransitionMap(receiver, FAST_HOLEY_DOUBLE_ELEMENTS); - case KeyedIC::LOAD: - case KeyedIC::STORE_NO_TRANSITION: - case KeyedIC::STORE_AND_GROW_NO_TRANSITION: + case STORE_NO_TRANSITION: + case STORE_AND_GROW_NO_TRANSITION: UNREACHABLE(); break; } @@ -1748,60 +1720,9 @@ Handle KeyedIC::ComputeTransitionedMap(Handle receiver, } -Handle KeyedStoreIC::GetElementStubWithoutMapCheck( - bool is_js_array, - ElementsKind elements_kind, - KeyedAccessGrowMode grow_mode) { - return KeyedStoreElementStub(is_js_array, elements_kind, grow_mode).GetCode(); -} - - -Handle KeyedStoreIC::ComputePolymorphicStub( - MapHandleList* receiver_maps, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode) { - // Collect MONOMORPHIC stubs for all target_receiver_maps. - CodeHandleList handler_ics(receiver_maps->length()); - MapHandleList transitioned_maps(receiver_maps->length()); - for (int i = 0; i < receiver_maps->length(); ++i) { - Handle receiver_map(receiver_maps->at(i)); - Handle cached_stub; - Handle transitioned_map = - receiver_map->FindTransitionedMap(receiver_maps); - - // TODO(mvstanton): The code below is doing pessimistic elements - // transitions. I would like to stop doing that and rely on Allocation Site - // Tracking to do a better job of ensuring the data types are what they need - // to be. Not all the elements are in place yet, pessimistic elements - // transitions are still important for performance. - if (!transitioned_map.is_null()) { - cached_stub = ElementsTransitionAndStoreStub( - receiver_map->elements_kind(), // original elements_kind - transitioned_map->elements_kind(), - receiver_map->instance_type() == JS_ARRAY_TYPE, // is_js_array - strict_mode, grow_mode).GetCode(); - } else { - cached_stub = ComputeMonomorphicStubWithoutMapCheck(receiver_map, - strict_mode, - grow_mode); - } - ASSERT(!cached_stub.is_null()); - handler_ics.Add(cached_stub); - transitioned_maps.Add(transitioned_map); - } - KeyedStoreStubCompiler compiler(isolate(), strict_mode, grow_mode); - Handle code = compiler.CompileStorePolymorphic( - receiver_maps, &handler_ics, &transitioned_maps); - isolate()->counters()->keyed_store_polymorphic_stubs()->Increment(); - PROFILE(isolate(), - CodeCreateEvent(Logger::KEYED_STORE_POLYMORPHIC_IC_TAG, *code, 0)); - return code; -} - - -KeyedIC::StubKind KeyedStoreIC::GetStubKind(Handle receiver, - Handle key, - Handle value) { +KeyedStoreIC::StubKind KeyedStoreIC::GetStubKind(Handle receiver, + Handle key, + Handle value) { ASSERT(key->IsSmi()); int index = Smi::cast(*key)->value(); bool allow_growth = receiver->IsJSArray() && @@ -1877,12 +1798,12 @@ MaybeObject* KeyedStoreIC::Store(State state, if (key->IsSymbol()) { Handle name = Handle::cast(key); - return IC::Store(state, - strict_mode, - object, - name, - value, - JSReceiver::MAY_BE_STORE_FROM_KEYED); + return StoreIC::Store(state, + strict_mode, + object, + name, + value, + JSReceiver::MAY_BE_STORE_FROM_KEYED); } // Do not use ICs for objects that require access checks (including @@ -1904,7 +1825,7 @@ MaybeObject* KeyedStoreIC::Store(State state, } else if (miss_mode != MISS_FORCE_GENERIC) { if (key->IsSmi() && (target() != *non_strict_arguments_stub())) { StubKind stub_kind = GetStubKind(receiver, key, value); - stub = ComputeStub(receiver, stub_kind, strict_mode, stub); + stub = StoreElementStub(receiver, stub_kind, strict_mode); } } else { TRACE_GENERIC_IC("KeyedStoreIC", "force generic"); diff --git a/src/ic.h b/src/ic.h index 784512a..aeb58bf 100644 --- a/src/ic.h +++ b/src/ic.h @@ -132,63 +132,7 @@ class IC { static inline JSObject* GetCodeCacheHolder(Object* object, InlineCacheHolderFlag holder); - MUST_USE_RESULT MaybeObject* Load(State state, - Handle object, - Handle name); - - MUST_USE_RESULT MaybeObject* Store( - State state, - StrictModeFlag strict_mode, - Handle object, - Handle name, - Handle value, - JSReceiver::StoreFromKeyed store_mode = - JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED); - protected: - virtual Handle pre_monomorphic_stub() { - UNREACHABLE(); - return Handle::null(); - } - virtual Handle megamorphic_stub() { - UNREACHABLE(); - return Handle::null(); - } - virtual Handle megamorphic_stub_strict() { - UNREACHABLE(); - return Handle::null(); - } - virtual Handle generic_stub() const { - UNREACHABLE(); - return Handle::null(); - } - virtual Code::Kind kind() const { - UNREACHABLE(); - return Code::STUB; - } - virtual Handle global_proxy_stub() { - UNREACHABLE(); - return Handle::null(); - } - virtual Handle global_proxy_stub_strict() { - UNREACHABLE(); - return Handle::null(); - } - - virtual void UpdateLoadCaches(LookupResult* lookup, - State state, - Handle object, - Handle name) { - UNREACHABLE(); - } - virtual void UpdateStoreCaches(LookupResult* lookup, - State state, - StrictModeFlag strict_mode, - Handle receiver, - Handle name, - Handle value) { - UNREACHABLE(); - } Address fp() const { return fp_; } Address pc() const { return *pc_address_; } Isolate* isolate() const { return isolate_; } @@ -376,7 +320,7 @@ class KeyedCallIC: public CallICBase { class LoadIC: public IC { public: explicit LoadIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { - ASSERT(target()->is_load_stub()); + ASSERT(target()->is_load_stub() || target()->is_keyed_load_stub()); } // Code generator routines. @@ -392,9 +336,18 @@ class LoadIC: public IC { static void GenerateArrayLength(MacroAssembler* masm); static void GenerateFunctionPrototype(MacroAssembler* masm); + MUST_USE_RESULT MaybeObject* Load(State state, + Handle object, + Handle name); + protected: virtual Code::Kind kind() const { return Code::LOAD_IC; } + virtual Handle generic_stub() const { + UNREACHABLE(); + return Handle::null(); + } + virtual Handle megamorphic_stub() { return isolate()->builtins()->LoadIC_Megamorphic(); } @@ -421,119 +374,15 @@ class LoadIC: public IC { }; -class KeyedIC: public IC { - public: - enum StubKind { - LOAD, - STORE_NO_TRANSITION, - STORE_TRANSITION_SMI_TO_OBJECT, - STORE_TRANSITION_SMI_TO_DOUBLE, - STORE_TRANSITION_DOUBLE_TO_OBJECT, - STORE_TRANSITION_HOLEY_SMI_TO_OBJECT, - STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE, - STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT, - STORE_AND_GROW_NO_TRANSITION, - STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT, - STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE, - STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT, - STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT, - STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE, - STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT - }; - - static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION - - STORE_NO_TRANSITION; - STATIC_ASSERT(kGrowICDelta == - STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT - - STORE_TRANSITION_SMI_TO_OBJECT); - STATIC_ASSERT(kGrowICDelta == - STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE - - STORE_TRANSITION_SMI_TO_DOUBLE); - STATIC_ASSERT(kGrowICDelta == - STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT - - STORE_TRANSITION_DOUBLE_TO_OBJECT); - - explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {} - virtual ~KeyedIC() {} - - static inline KeyedAccessGrowMode GetGrowModeFromStubKind( - StubKind stub_kind) { - return (stub_kind >= STORE_AND_GROW_NO_TRANSITION) - ? ALLOW_JSARRAY_GROWTH - : DO_NOT_ALLOW_JSARRAY_GROWTH; - } - - static inline StubKind GetGrowStubKind(StubKind stub_kind) { - ASSERT(stub_kind != LOAD); - if (stub_kind < STORE_AND_GROW_NO_TRANSITION) { - stub_kind = static_cast(static_cast(stub_kind) + - kGrowICDelta); - } - return stub_kind; - } - - virtual Handle GetElementStubWithoutMapCheck( - bool is_js_array, - ElementsKind elements_kind, - KeyedAccessGrowMode grow_mode) = 0; - - protected: - virtual Handle string_stub() { - return Handle::null(); - } - - Handle ComputeStub(Handle receiver, - StubKind stub_kind, - StrictModeFlag strict_mode, - Handle default_stub); - - virtual Handle ComputePolymorphicStub( - MapHandleList* receiver_maps, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode) = 0; - - Handle ComputeMonomorphicStubWithoutMapCheck( - Handle receiver_map, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode); - - private: - void GetReceiverMapsForStub(Handle stub, MapHandleList* result); - - Handle ComputeMonomorphicStub(Handle receiver_map, - StubKind stub_kind, - StrictModeFlag strict_mode, - Handle default_stub); - - Handle ComputeTransitionedMap(Handle receiver, - StubKind stub_kind); - - static bool IsTransitionStubKind(StubKind stub_kind) { - return stub_kind > STORE_NO_TRANSITION && - stub_kind != STORE_AND_GROW_NO_TRANSITION; - } - - static bool IsGrowStubKind(StubKind stub_kind) { - return stub_kind >= STORE_AND_GROW_NO_TRANSITION; - } - - static StubKind GetNoTransitionStubKind(StubKind stub_kind) { - if (!IsTransitionStubKind(stub_kind)) return stub_kind; - if (IsGrowStubKind(stub_kind)) return STORE_AND_GROW_NO_TRANSITION; - return STORE_NO_TRANSITION; - } -}; - - enum ICMissMode { MISS_FORCE_GENERIC, MISS }; -class KeyedLoadIC: public KeyedIC { +class KeyedLoadIC: public LoadIC { public: - explicit KeyedLoadIC(Isolate* isolate) : KeyedIC(isolate) { + explicit KeyedLoadIC(Isolate* isolate) : LoadIC(isolate) { ASSERT(target()->is_keyed_load_stub()); } @@ -563,14 +412,11 @@ class KeyedLoadIC: public KeyedIC { static const int kSlowCaseBitFieldMask = (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor); - virtual Handle GetElementStubWithoutMapCheck( - bool is_js_array, - ElementsKind elements_kind, - KeyedAccessGrowMode grow_mode); - protected: virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; } + Handle LoadElementStub(Handle receiver); + virtual Handle megamorphic_stub() { return isolate()->builtins()->KeyedLoadIC_Generic(); } @@ -578,14 +424,6 @@ class KeyedLoadIC: public KeyedIC { return isolate()->builtins()->KeyedLoadIC_Generic(); } - virtual Handle ComputePolymorphicStub(MapHandleList* receiver_maps, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode); - - virtual Handle string_stub() { - return isolate()->builtins()->KeyedLoadIC_String(); - } - // Update the inline cache. virtual void UpdateLoadCaches(LookupResult* lookup, State state, @@ -606,6 +444,9 @@ class KeyedLoadIC: public KeyedIC { Handle non_strict_arguments_stub() { return isolate()->builtins()->KeyedLoadIC_NonStrictArguments(); } + Handle string_stub() { + return isolate()->builtins()->KeyedLoadIC_String(); + } static void Clear(Address address, Code* target); @@ -616,7 +457,7 @@ class KeyedLoadIC: public KeyedIC { class StoreIC: public IC { public: explicit StoreIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { - ASSERT(target()->is_store_stub()); + ASSERT(target()->is_store_stub() || target()->is_keyed_store_stub()); } // Code generators for stub routines. Only called once at startup. @@ -629,6 +470,15 @@ class StoreIC: public IC { static void GenerateGlobalProxy(MacroAssembler* masm, StrictModeFlag strict_mode); + MUST_USE_RESULT MaybeObject* Store( + State state, + StrictModeFlag strict_mode, + Handle object, + Handle name, + Handle value, + JSReceiver::StoreFromKeyed store_mode = + JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED); + protected: virtual Code::Kind kind() const { return Code::STORE_IC; } virtual Handle megamorphic_stub() { @@ -687,9 +537,46 @@ enum KeyedStoreIncrementLength { }; -class KeyedStoreIC: public KeyedIC { +class KeyedStoreIC: public StoreIC { public: - explicit KeyedStoreIC(Isolate* isolate) : KeyedIC(isolate) { + enum StubKind { + STORE_NO_TRANSITION, + STORE_TRANSITION_SMI_TO_OBJECT, + STORE_TRANSITION_SMI_TO_DOUBLE, + STORE_TRANSITION_DOUBLE_TO_OBJECT, + STORE_TRANSITION_HOLEY_SMI_TO_OBJECT, + STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE, + STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT, + STORE_AND_GROW_NO_TRANSITION, + STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT, + STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE, + STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT, + STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT, + STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE, + STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT + }; + + static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION - + STORE_NO_TRANSITION; + STATIC_ASSERT(kGrowICDelta == + STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT - + STORE_TRANSITION_SMI_TO_OBJECT); + STATIC_ASSERT(kGrowICDelta == + STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE - + STORE_TRANSITION_SMI_TO_DOUBLE); + STATIC_ASSERT(kGrowICDelta == + STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT - + STORE_TRANSITION_DOUBLE_TO_OBJECT); + + static inline StubKind GetGrowStubKind(StubKind stub_kind) { + if (stub_kind < STORE_AND_GROW_NO_TRANSITION) { + stub_kind = static_cast(static_cast(stub_kind) + + kGrowICDelta); + } + return stub_kind; + } + + explicit KeyedStoreIC(Isolate* isolate) : StoreIC(isolate) { ASSERT(target()->is_keyed_store_stub()); } @@ -713,18 +600,9 @@ class KeyedStoreIC: public KeyedIC { static void GenerateTransitionElementsSmiToDouble(MacroAssembler* masm); static void GenerateTransitionElementsDoubleToObject(MacroAssembler* masm); - virtual Handle GetElementStubWithoutMapCheck( - bool is_js_array, - ElementsKind elements_kind, - KeyedAccessGrowMode grow_mode); - protected: virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; } - virtual Handle ComputePolymorphicStub(MapHandleList* receiver_maps, - StrictModeFlag strict_mode, - KeyedAccessGrowMode grow_mode); - // Update the inline cache. virtual void UpdateStoreCaches(LookupResult* lookup, State state, @@ -740,6 +618,10 @@ class KeyedStoreIC: public KeyedIC { return isolate()->builtins()->KeyedStoreIC_Generic_Strict(); } + Handle StoreElementStub(Handle receiver, + StubKind stub_kind, + StrictModeFlag strict_mode); + private: void set_target(Code* code) { // Strict mode must be preserved across IC patching. @@ -771,6 +653,24 @@ class KeyedStoreIC: public KeyedIC { Handle key, Handle value); + static bool IsTransitionStubKind(StubKind stub_kind) { + return stub_kind > STORE_NO_TRANSITION && + stub_kind != STORE_AND_GROW_NO_TRANSITION; + } + + static bool IsGrowStubKind(StubKind stub_kind) { + return stub_kind >= STORE_AND_GROW_NO_TRANSITION; + } + + static StubKind GetNoTransitionStubKind(StubKind stub_kind) { + if (!IsTransitionStubKind(stub_kind)) return stub_kind; + if (IsGrowStubKind(stub_kind)) return STORE_AND_GROW_NO_TRANSITION; + return STORE_NO_TRANSITION; + } + + Handle ComputeTransitionedMap(Handle receiver, + StubKind stub_kind); + friend class IC; }; diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 08954ba..73272a2 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -371,69 +371,48 @@ Handle StubCache::ComputeStoreField(Handle name, } -Handle StubCache::ComputeKeyedLoadOrStoreElement( +Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { + Code::Flags flags = + Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, Code::NORMAL); + Handle name = + isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); + + Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); + if (probe->IsCode()) return Handle::cast(probe); + + KeyedLoadStubCompiler compiler(isolate()); + Handle code = compiler.CompileLoadElement(receiver_map); + + PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); + Map::UpdateCodeCache(receiver_map, name, code); + return code; +} + + +Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, - KeyedIC::StubKind stub_kind, - StrictModeFlag strict_mode) { - KeyedAccessGrowMode grow_mode = - KeyedIC::GetGrowModeFromStubKind(stub_kind); + KeyedStoreIC::StubKind stub_kind, + StrictModeFlag strict_mode, + KeyedAccessGrowMode grow_mode) { Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); - Code::Flags flags = - Code::ComputeMonomorphicFlags( - stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC - : Code::KEYED_STORE_IC, - Code::NORMAL, - extra_state); - Handle name; - switch (stub_kind) { - case KeyedIC::LOAD: - name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); - break; - case KeyedIC::STORE_NO_TRANSITION: - name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); - break; - case KeyedIC::STORE_AND_GROW_NO_TRANSITION: - name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); - break; - default: - UNREACHABLE(); - break; - } + Code::Flags flags = Code::ComputeMonomorphicFlags( + Code::KEYED_STORE_IC, Code::NORMAL, extra_state); + + ASSERT(stub_kind == KeyedStoreIC::STORE_NO_TRANSITION || + stub_kind == KeyedStoreIC::STORE_AND_GROW_NO_TRANSITION); + + Handle name = stub_kind == KeyedStoreIC::STORE_NO_TRANSITION + ? isolate()->factory()->KeyedStoreElementMonomorphic_symbol() + : isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); + Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); - Handle code; - switch (stub_kind) { - case KeyedIC::LOAD: { - KeyedLoadStubCompiler compiler(isolate_); - code = compiler.CompileLoadElement(receiver_map); - break; - } - case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { - KeyedStoreStubCompiler compiler(isolate_, strict_mode, - ALLOW_JSARRAY_GROWTH); - code = compiler.CompileStoreElement(receiver_map); - break; - } - case KeyedIC::STORE_NO_TRANSITION: { - KeyedStoreStubCompiler compiler(isolate_, strict_mode, - DO_NOT_ALLOW_JSARRAY_GROWTH); - code = compiler.CompileStoreElement(receiver_map); - break; - } - default: - UNREACHABLE(); - break; - } - - ASSERT(!code.is_null()); + KeyedStoreStubCompiler compiler(isolate(), strict_mode, grow_mode); + Handle code = compiler.CompileStoreElement(receiver_map); - if (stub_kind == KeyedIC::LOAD) { - PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); - } else { - PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); - } + PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); Map::UpdateCodeCache(receiver_map, name, code); return code; } @@ -851,6 +830,41 @@ Handle StubCache::ComputeCallMiss(int argc, } +Handle StubCache::ComputeLoadElementPolymorphic( + MapHandleList* receiver_maps) { + Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); + Handle cache = + isolate_->factory()->polymorphic_code_cache(); + Handle probe = cache->Lookup(receiver_maps, flags); + if (probe->IsCode()) return Handle::cast(probe); + + KeyedLoadStubCompiler compiler(isolate_); + Handle code = compiler.CompileLoadElementPolymorphic(receiver_maps); + PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); + return code; +} + + +Handle StubCache::ComputeStoreElementPolymorphic( + MapHandleList* receiver_maps, + KeyedAccessGrowMode grow_mode, + StrictModeFlag strict_mode) { + Handle cache = + isolate_->factory()->polymorphic_code_cache(); + Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, + strict_mode); + Code::Flags flags = + Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); + Handle probe = cache->Lookup(receiver_maps, flags); + if (probe->IsCode()) return Handle::cast(probe); + + KeyedStoreStubCompiler compiler(isolate_, strict_mode, grow_mode); + Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); + PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); + return code; +} + + #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { @@ -1366,6 +1380,40 @@ Handle KeyedLoadStubCompiler::GetCode(Code::StubType type, } +Handle KeyedLoadStubCompiler::CompileLoadElementPolymorphic( + MapHandleList* receiver_maps) { + CodeHandleList handler_ics(receiver_maps->length()); + for (int i = 0; i < receiver_maps->length(); ++i) { + Handle receiver_map = receiver_maps->at(i); + Handle cached_stub; + + if ((receiver_map->instance_type() & kNotStringTag) == 0) { + cached_stub = isolate()->builtins()->KeyedLoadIC_String(); + } else { + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + ElementsKind elements_kind = receiver_map->elements_kind(); + + if (IsFastElementsKind(elements_kind) || + IsExternalArrayElementsKind(elements_kind)) { + cached_stub = + KeyedLoadFastElementStub(is_js_array, elements_kind).GetCode(); + } else { + ASSERT(elements_kind == DICTIONARY_ELEMENTS); + cached_stub = KeyedLoadDictionaryElementStub().GetCode(); + } + } + + handler_ics.Add(cached_stub); + } + Handle code = CompileLoadPolymorphic(receiver_maps, &handler_ics); + isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); + PROFILE(isolate(), + CodeCreateEvent(Logger::KEYED_LOAD_POLYMORPHIC_IC_TAG, *code, 0)); + return code; +} + + + Handle StoreStubCompiler::GetCode(Code::StubType type, Handle name) { Code::Flags flags = @@ -1391,6 +1439,50 @@ Handle KeyedStoreStubCompiler::GetCode(Code::StubType type, } +Handle KeyedStoreStubCompiler::CompileStoreElementPolymorphic( + MapHandleList* receiver_maps) { + // Collect MONOMORPHIC stubs for all |receiver_maps|. + CodeHandleList handler_ics(receiver_maps->length()); + MapHandleList transitioned_maps(receiver_maps->length()); + for (int i = 0; i < receiver_maps->length(); ++i) { + Handle receiver_map(receiver_maps->at(i)); + Handle cached_stub; + Handle transitioned_map = + receiver_map->FindTransitionedMap(receiver_maps); + + // TODO(mvstanton): The code below is doing pessimistic elements + // transitions. I would like to stop doing that and rely on Allocation Site + // Tracking to do a better job of ensuring the data types are what they need + // to be. Not all the elements are in place yet, pessimistic elements + // transitions are still important for performance. + bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; + ElementsKind elements_kind = receiver_map->elements_kind(); + if (!transitioned_map.is_null()) { + cached_stub = ElementsTransitionAndStoreStub( + elements_kind, + transitioned_map->elements_kind(), + is_js_array, + strict_mode_, + grow_mode_).GetCode(); + } else { + cached_stub = KeyedStoreElementStub( + is_js_array, + elements_kind, + grow_mode_).GetCode(); + } + ASSERT(!cached_stub.is_null()); + handler_ics.Add(cached_stub); + transitioned_maps.Add(transitioned_map); + } + Handle code = + CompileStorePolymorphic(receiver_maps, &handler_ics, &transitioned_maps); + isolate()->counters()->keyed_store_polymorphic_stubs()->Increment(); + PROFILE(isolate(), + CodeCreateEvent(Logger::KEYED_STORE_POLYMORPHIC_IC_TAG, *code, 0)); + return code; +} + + void KeyedStoreStubCompiler::GenerateStoreDictionaryElement( MacroAssembler* masm) { KeyedStoreIC::GenerateSlow(masm); diff --git a/src/stub-cache.h b/src/stub-cache.h index 2b12321..b26519c 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -172,9 +172,12 @@ class StubCache { Handle transition, StrictModeFlag strict_mode); - Handle ComputeKeyedLoadOrStoreElement(Handle receiver_map, - KeyedIC::StubKind stub_kind, - StrictModeFlag strict_mode); + Handle ComputeKeyedLoadElement(Handle receiver_map); + + Handle ComputeKeyedStoreElement(Handle receiver_map, + KeyedStoreIC::StubKind stub_kind, + StrictModeFlag strict_mode, + KeyedAccessGrowMode grow_mode); // --- @@ -234,6 +237,13 @@ class StubCache { Code::Kind kind, Code::ExtraICState state); + // --- + + Handle ComputeLoadElementPolymorphic(MapHandleList* receiver_maps); + Handle ComputeStoreElementPolymorphic(MapHandleList* receiver_maps, + KeyedAccessGrowMode grow_mode, + StrictModeFlag strict_mode); + // Finds the Code object stored in the Heap::non_monomorphic_cache(). Code* FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind); @@ -666,6 +676,8 @@ class KeyedLoadStubCompiler: public StubCompiler { Handle CompileLoadPolymorphic(MapHandleList* receiver_maps, CodeHandleList* handler_ics); + Handle CompileLoadElementPolymorphic(MapHandleList* receiver_maps); + static void GenerateLoadDictionaryElement(MacroAssembler* masm); private: @@ -733,6 +745,8 @@ class KeyedStoreStubCompiler: public StubCompiler { CodeHandleList* handler_stubs, MapHandleList* transitioned_maps); + Handle CompileStoreElementPolymorphic(MapHandleList* receiver_maps); + static void GenerateStoreFastElement(MacroAssembler* masm, bool is_js_array, ElementsKind element_kind, -- 2.7.4