| Index: src/ic.cc
|
| diff --git a/src/ic.cc b/src/ic.cc
|
| index 7fdf6be31fbe5f36f53a22182371963d5e212be1..33cd3cb53b7055dbb4808e94d1576cc371ff5275 100644
|
| --- a/src/ic.cc
|
| +++ b/src/ic.cc
|
| @@ -59,6 +59,17 @@ char IC::TransitionMarkFromState(IC::State state) {
|
| return 0;
|
| }
|
|
|
| +
|
| +const char* GetTransitionMarkModifier(KeyedAccessStoreMode mode) {
|
| + if (mode == STORE_NO_TRANSITION_HANDLE_COW) return ".COW";
|
| + if (mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
|
| + return ".IGNORE_OOB";
|
| + }
|
| + if (IsGrowStoreMode(mode)) return ".GROW";
|
| + return "";
|
| +}
|
| +
|
| +
|
| void IC::TraceIC(const char* type,
|
| Handle<Object> name,
|
| State old_state,
|
| @@ -81,13 +92,13 @@ void IC::TraceIC(const char* type,
|
| }
|
| }
|
| JavaScriptFrame::PrintTop(isolate, stdout, false, true);
|
| - bool new_can_grow =
|
| - Code::GetKeyedAccessGrowMode(new_target->extra_ic_state()) ==
|
| - ALLOW_JSARRAY_GROWTH;
|
| + Code::ExtraICState state = new_target->extra_ic_state();
|
| + const char* modifier =
|
| + GetTransitionMarkModifier(Code::GetKeyedAccessStoreMode(state));
|
| PrintF(" (%c->%c%s)",
|
| TransitionMarkFromState(old_state),
|
| TransitionMarkFromState(new_state),
|
| - new_can_grow ? ".GROW" : "");
|
| + modifier);
|
| name->Print();
|
| PrintF("]\n");
|
| }
|
| @@ -1602,13 +1613,8 @@ Handle<Code> StoreIC::ComputeStoreMonomorphic(LookupResult* lookup,
|
|
|
|
|
| Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
|
| - StubKind stub_kind,
|
| + KeyedAccessStoreMode store_mode,
|
| StrictModeFlag strict_mode) {
|
| - State ic_state = target()->ic_state();
|
| - KeyedAccessGrowMode grow_mode = IsGrowStubKind(stub_kind)
|
| - ? ALLOW_JSARRAY_GROWTH
|
| - : DO_NOT_ALLOW_JSARRAY_GROWTH;
|
| -
|
| // 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.
|
| @@ -1617,42 +1623,76 @@ Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
|
| return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub();
|
| }
|
|
|
| + if ((store_mode == STORE_NO_TRANSITION_HANDLE_COW ||
|
| + store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS)) {
|
| + // TODO(danno): We'll soon handle MONOMORPHIC ICs that also support
|
| + // copying COW arrays and silently ignoring some OOB stores into external
|
| + // arrays, but for now use the generic.
|
| + TRACE_GENERIC_IC(isolate(), "KeyedIC", "COW/OOB external array");
|
| + return strict_mode == kStrictMode
|
| + ? generic_stub_strict()
|
| + : generic_stub();
|
| + }
|
| +
|
| + State ic_state = target()->ic_state();
|
| Handle<Map> 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.
|
| - Handle<Map> monomorphic_map = ComputeTransitionedMap(receiver, stub_kind);
|
| - stub_kind = GetNoTransitionStubKind(stub_kind);
|
| + Handle<Map> monomorphic_map = ComputeTransitionedMap(receiver, store_mode);
|
| + store_mode = GetNonTransitioningStoreMode(store_mode);
|
| return isolate()->stub_cache()->ComputeKeyedStoreElement(
|
| - monomorphic_map, stub_kind, strict_mode, grow_mode);
|
| + monomorphic_map, strict_mode, store_mode);
|
| }
|
|
|
| - GetReceiverMapsForStub(Handle<Code>(target()), &target_receiver_maps);
|
| + MapHandleList target_receiver_maps;
|
| + target()->FindAllMaps(&target_receiver_maps);
|
| if (target_receiver_maps.length() == 0) {
|
| - // Optimistically assume that ICs that haven't reached the MONOMORPHIC state
|
| - // yet will do so and stay there.
|
| - stub_kind = GetNoTransitionStubKind(stub_kind);
|
| - return isolate()->stub_cache()->ComputeKeyedStoreElement(
|
| - receiver_map, stub_kind, strict_mode, grow_mode);
|
| + // In the case that there is a non-map-specific IC is installed (e.g. keyed
|
| + // stores into properties in dictionary mode), then there will be not
|
| + // receiver maps in the target.
|
| + return strict_mode == kStrictMode
|
| + ? generic_stub_strict()
|
| + : generic_stub();
|
| }
|
| - // 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())) {
|
| - Handle<Map> 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);
|
| +
|
| + // There are several special cases where an IC that is MONOMORPHIC can still
|
| + // transition to a different GetNonTransitioningStoreMode IC that handles a
|
| + // superset of the original IC. Handle those here if the receiver map hasn't
|
| + // changed or it has transitioned to a more general kind.
|
| + KeyedAccessStoreMode old_store_mode =
|
| + Code::GetKeyedAccessStoreMode(target()->extra_ic_state());
|
| + Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
|
| + if (ic_state == MONOMORPHIC && old_store_mode == STANDARD_STORE) {
|
| + // If the "old" and "new" maps are in the same elements map family, stay
|
| + // MONOMORPHIC and use the map for the most generic ElementsKind.
|
| + Handle<Map> transitioned_receiver_map = receiver_map;
|
| + if (IsTransitionStoreMode(store_mode)) {
|
| + transitioned_receiver_map =
|
| + ComputeTransitionedMap(receiver, store_mode);
|
| + }
|
| + ElementsKind transitioned_kind =
|
| + transitioned_receiver_map->elements_kind();
|
| + bool more_general_transition =
|
| + IsMoreGeneralElementsKindTransition(
|
| + previous_receiver_map->elements_kind(),
|
| + transitioned_kind);
|
| + Map* transitioned_previous_map = more_general_transition
|
| + ? previous_receiver_map->LookupElementsTransitionMap(transitioned_kind)
|
| + : NULL;
|
| + if (transitioned_previous_map == *transitioned_receiver_map) {
|
| + // Element family is the same, use the "worst" case map.
|
| + store_mode = GetNonTransitioningStoreMode(store_mode);
|
| + return isolate()->stub_cache()->ComputeKeyedStoreElement(
|
| + transitioned_receiver_map, strict_mode, store_mode);
|
| + } else if (*previous_receiver_map == receiver->map()) {
|
| + if (IsGrowStoreMode(store_mode)) {
|
| + // A "normal" IC that handles stores can switch to a version that can
|
| + // grow at the end of the array and still stay MONOMORPHIC.
|
| + return isolate()->stub_cache()->ComputeKeyedStoreElement(
|
| + receiver_map, strict_mode, store_mode);
|
| + }
|
| + }
|
| }
|
|
|
| ASSERT(ic_state != GENERIC);
|
| @@ -1660,9 +1700,11 @@ Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
|
| bool map_added =
|
| AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map);
|
|
|
| - if (IsTransitionStubKind(stub_kind)) {
|
| - Handle<Map> new_map = ComputeTransitionedMap(receiver, stub_kind);
|
| - map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps, new_map);
|
| + if (IsTransitionStoreMode(store_mode)) {
|
| + Handle<Map> transitioned_receiver_map =
|
| + ComputeTransitionedMap(receiver, store_mode);
|
| + map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps,
|
| + transitioned_receiver_map);
|
| }
|
|
|
| if (!map_added) {
|
| @@ -1679,19 +1721,29 @@ Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
|
| return strict_mode == kStrictMode ? generic_stub_strict() : generic_stub();
|
| }
|
|
|
| - if ((Code::GetKeyedAccessGrowMode(target()->extra_ic_state()) ==
|
| - ALLOW_JSARRAY_GROWTH)) {
|
| - grow_mode = ALLOW_JSARRAY_GROWTH;
|
| + // Make sure all polymorphic handlers have the same store mode, otherwise the
|
| + // generic stub must be used.
|
| + store_mode = GetNonTransitioningStoreMode(store_mode);
|
| + if (old_store_mode != STANDARD_STORE) {
|
| + if (store_mode == STANDARD_STORE) {
|
| + store_mode = old_store_mode;
|
| + } else if (store_mode != old_store_mode) {
|
| + TRACE_GENERIC_IC(isolate(), "KeyedIC", "store mode mismatch");
|
| + return strict_mode == kStrictMode
|
| + ? generic_stub_strict()
|
| + : generic_stub();
|
| + }
|
| }
|
|
|
| return isolate()->stub_cache()->ComputeStoreElementPolymorphic(
|
| - &target_receiver_maps, grow_mode, strict_mode);
|
| + &target_receiver_maps, store_mode, strict_mode);
|
| }
|
|
|
|
|
| -Handle<Map> KeyedStoreIC::ComputeTransitionedMap(Handle<JSObject> receiver,
|
| - StubKind stub_kind) {
|
| - switch (stub_kind) {
|
| +Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
|
| + Handle<JSObject> receiver,
|
| + KeyedAccessStoreMode store_mode) {
|
| + switch (store_mode) {
|
| case STORE_TRANSITION_SMI_TO_OBJECT:
|
| case STORE_TRANSITION_DOUBLE_TO_OBJECT:
|
| case STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT:
|
| @@ -1710,7 +1762,11 @@ Handle<Map> KeyedStoreIC::ComputeTransitionedMap(Handle<JSObject> receiver,
|
| case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE:
|
| return JSObject::GetElementsTransitionMap(receiver,
|
| FAST_HOLEY_DOUBLE_ELEMENTS);
|
| - case STORE_NO_TRANSITION:
|
| + case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
|
| + ASSERT(receiver->map()->has_external_array_elements());
|
| + // Fall through
|
| + case STORE_NO_TRANSITION_HANDLE_COW:
|
| + case STANDARD_STORE:
|
| case STORE_AND_GROW_NO_TRANSITION:
|
| return Handle<Map>(receiver->map());
|
| }
|
| @@ -1718,15 +1774,23 @@ Handle<Map> KeyedStoreIC::ComputeTransitionedMap(Handle<JSObject> receiver,
|
| }
|
|
|
|
|
| -KeyedStoreIC::StubKind KeyedStoreIC::GetStubKind(Handle<JSObject> receiver,
|
| - Handle<Object> key,
|
| - Handle<Object> value) {
|
| +bool IsOutOfBoundsAccess(Handle<JSObject> receiver,
|
| + int index) {
|
| + if (receiver->IsJSArray()) {
|
| + return JSArray::cast(*receiver)->length()->IsSmi() &&
|
| + index >= Smi::cast(JSArray::cast(*receiver)->length())->value();
|
| + }
|
| + return index >= receiver->elements()->length();
|
| +}
|
| +
|
| +
|
| +KeyedAccessStoreMode KeyedStoreIC::GetStoreMode(Handle<JSObject> receiver,
|
| + Handle<Object> key,
|
| + Handle<Object> value) {
|
| ASSERT(key->IsSmi());
|
| int index = Smi::cast(*key)->value();
|
| - bool allow_growth = receiver->IsJSArray() &&
|
| - JSArray::cast(*receiver)->length()->IsSmi() &&
|
| - index >= Smi::cast(JSArray::cast(*receiver)->length())->value();
|
| -
|
| + bool oob_access = IsOutOfBoundsAccess(receiver, index);
|
| + bool allow_growth = receiver->IsJSArray() && oob_access;
|
| if (allow_growth) {
|
| // Handle growing array in stub if necessary.
|
| if (receiver->HasFastSmiElements()) {
|
| @@ -1779,7 +1843,12 @@ KeyedStoreIC::StubKind KeyedStoreIC::GetStubKind(Handle<JSObject> receiver,
|
| }
|
| }
|
| }
|
| - return STORE_NO_TRANSITION;
|
| + if (!FLAG_trace_external_array_abuse &&
|
| + receiver->map()->has_external_array_elements() && oob_access) {
|
| + return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
|
| + } else {
|
| + return STANDARD_STORE;
|
| + }
|
| }
|
| }
|
|
|
| @@ -1819,8 +1888,8 @@ MaybeObject* KeyedStoreIC::Store(State state,
|
| isolate()->heap()->non_strict_arguments_elements_map()) {
|
| stub = non_strict_arguments_stub();
|
| } else if (key->IsSmi() && (target() != *non_strict_arguments_stub())) {
|
| - StubKind stub_kind = GetStubKind(receiver, key, value);
|
| - stub = StoreElementStub(receiver, stub_kind, strict_mode);
|
| + KeyedAccessStoreMode store_mode = GetStoreMode(receiver, key, value);
|
| + stub = StoreElementStub(receiver, store_mode, strict_mode);
|
| }
|
| }
|
| } else {
|
|
|