[ic] Decouple transitions from KeyedAccessStoreMode

KeyedAccessStoreMode is used when creating store handlers to determine
which cases to handle (ex: handle cow, grow) and hence choose an
approriate builtin. It is also used to specify elements kind transitions
which is used when computing ic transitions. The store mode and the
transition mode are independent of each other and it would be cleaner to
use different enums to represent them. This also reduces the total number
of cases in each enum.

Change-Id: I96aeff7a765b312b8088e831776743c67c533bd9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1619751
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Mythri Alle <mythria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61729}
This commit is contained in:
Mythri A 2019-05-22 09:49:45 +01:00 committed by Commit Bot
parent 45bfa681f9
commit 9188d54941
8 changed files with 102 additions and 166 deletions

View File

@ -243,17 +243,17 @@ TF_BUILTIN(ElementsTransitionAndStore_Standard, HandlerBuiltinsAssembler) {
TF_BUILTIN(ElementsTransitionAndStore_GrowNoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_AND_GROW_NO_TRANSITION_HANDLE_COW);
Generate_ElementsTransitionAndStore(STORE_AND_GROW_HANDLE_COW);
}
TF_BUILTIN(ElementsTransitionAndStore_NoTransitionIgnoreOOB,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS);
Generate_ElementsTransitionAndStore(STORE_IGNORE_OUT_OF_BOUNDS);
}
TF_BUILTIN(ElementsTransitionAndStore_NoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_ElementsTransitionAndStore(STORE_NO_TRANSITION_HANDLE_COW);
Generate_ElementsTransitionAndStore(STORE_HANDLE_COW);
}
// All elements kinds handled by EmitElementStore. Specifically, this includes
@ -353,15 +353,15 @@ TF_BUILTIN(StoreFastElementIC_Standard, HandlerBuiltinsAssembler) {
TF_BUILTIN(StoreFastElementIC_GrowNoTransitionHandleCOW,
HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_AND_GROW_NO_TRANSITION_HANDLE_COW);
Generate_StoreFastElementIC(STORE_AND_GROW_HANDLE_COW);
}
TF_BUILTIN(StoreFastElementIC_NoTransitionIgnoreOOB, HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS);
Generate_StoreFastElementIC(STORE_IGNORE_OUT_OF_BOUNDS);
}
TF_BUILTIN(StoreFastElementIC_NoTransitionHandleCOW, HandlerBuiltinsAssembler) {
Generate_StoreFastElementIC(STORE_NO_TRANSITION_HANDLE_COW);
Generate_StoreFastElementIC(STORE_HANDLE_COW);
}
TF_BUILTIN(LoadGlobalIC_Slow, CodeStubAssembler) {

View File

@ -104,15 +104,15 @@ Callable CodeFactory::KeyedStoreIC_SloppyArguments(Isolate* isolate,
case STANDARD_STORE:
builtin_index = Builtins::kKeyedStoreIC_SloppyArguments_Standard;
break;
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
builtin_index =
Builtins::kKeyedStoreIC_SloppyArguments_GrowNoTransitionHandleCOW;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
builtin_index =
Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB;
break;
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
builtin_index =
Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW;
break;
@ -129,13 +129,13 @@ Callable CodeFactory::KeyedStoreIC_Slow(Isolate* isolate,
case STANDARD_STORE:
builtin_index = Builtins::kKeyedStoreIC_Slow_Standard;
break;
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
builtin_index = Builtins::kKeyedStoreIC_Slow_GrowNoTransitionHandleCOW;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
builtin_index = Builtins::kKeyedStoreIC_Slow_NoTransitionIgnoreOOB;
break;
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
builtin_index = Builtins::kKeyedStoreIC_Slow_NoTransitionHandleCOW;
break;
default:
@ -151,15 +151,15 @@ Callable CodeFactory::StoreInArrayLiteralIC_Slow(Isolate* isolate,
case STANDARD_STORE:
builtin_index = Builtins::kStoreInArrayLiteralIC_Slow_Standard;
break;
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
builtin_index =
Builtins::kStoreInArrayLiteralIC_Slow_GrowNoTransitionHandleCOW;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
builtin_index =
Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionIgnoreOOB;
break;
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
builtin_index =
Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionHandleCOW;
break;
@ -176,15 +176,15 @@ Callable CodeFactory::ElementsTransitionAndStore(Isolate* isolate,
case STANDARD_STORE:
builtin_index = Builtins::kElementsTransitionAndStore_Standard;
break;
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
builtin_index =
Builtins::kElementsTransitionAndStore_GrowNoTransitionHandleCOW;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
builtin_index =
Builtins::kElementsTransitionAndStore_NoTransitionIgnoreOOB;
break;
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
builtin_index =
Builtins::kElementsTransitionAndStore_NoTransitionHandleCOW;
break;
@ -201,13 +201,13 @@ Callable CodeFactory::StoreFastElementIC(Isolate* isolate,
case STANDARD_STORE:
builtin_index = Builtins::kStoreFastElementIC_Standard;
break;
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
builtin_index = Builtins::kStoreFastElementIC_GrowNoTransitionHandleCOW;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
builtin_index = Builtins::kStoreFastElementIC_NoTransitionIgnoreOOB;
break;
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
builtin_index = Builtins::kStoreFastElementIC_NoTransitionHandleCOW;
break;
default:

View File

@ -10609,7 +10609,7 @@ void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value,
// Bounds check.
TNode<UintPtrT> length = LoadJSTypedArrayLength(CAST(object));
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
if (store_mode == STORE_IGNORE_OUT_OF_BOUNDS) {
// Skip the store if we write beyond the length or
// to a property with a negative integer index.
GotoIfNot(UintPtrLessThan(intptr_key, length), &done);

View File

@ -2618,7 +2618,7 @@ JSNativeContextSpecialization::BuildElementAccess(
}
if (load_mode == LOAD_IGNORE_OUT_OF_BOUNDS ||
store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
store_mode == STORE_IGNORE_OUT_OF_BOUNDS) {
// Only check that the {index} is in SignedSmall range. We do the actual
// bounds check below and just skip the property access if it's out of
// bounds for the {receiver}.
@ -2702,7 +2702,7 @@ JSNativeContextSpecialization::BuildElementAccess(
}
// Check if we can skip the out-of-bounds store.
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
if (store_mode == STORE_IGNORE_OUT_OF_BOUNDS) {
Node* check =
graph()->NewNode(simplified()->NumberLessThan(), index, length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
@ -2995,7 +2995,7 @@ JSNativeContextSpecialization::BuildElementAccess(
// Ensure that copy-on-write backing store is writable.
if (IsSmiOrObjectElementsKind(elements_kind) &&
store_mode == STORE_NO_TRANSITION_HANDLE_COW) {
store_mode == STORE_HANDLE_COW) {
elements = effect =
graph()->NewNode(simplified()->EnsureWritableFastElements(),
receiver, elements, effect, control);
@ -3037,7 +3037,7 @@ JSNativeContextSpecialization::BuildElementAccess(
// If we didn't grow {elements}, it might still be COW, in which case we
// copy it now.
if (IsSmiOrObjectElementsKind(elements_kind) &&
store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW) {
store_mode == STORE_AND_GROW_HANDLE_COW) {
elements = effect =
graph()->NewNode(simplified()->EnsureWritableFastElements(),
receiver, elements, effect, control);

View File

@ -1153,19 +1153,19 @@ KeyedAccessStoreMode KeyedAccessStoreModeForBuiltin(int builtin_index) {
case Builtins::kKeyedStoreIC_Slow_GrowNoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_GrowNoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_GrowNoTransitionHandleCOW:
return STORE_AND_GROW_NO_TRANSITION_HANDLE_COW;
return STORE_AND_GROW_HANDLE_COW;
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreOOB:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kKeyedStoreIC_Slow_NoTransitionIgnoreOOB:
case Builtins::kStoreFastElementIC_NoTransitionIgnoreOOB:
case Builtins::kElementsTransitionAndStore_NoTransitionIgnoreOOB:
return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
return STORE_IGNORE_OUT_OF_BOUNDS;
case Builtins::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
case Builtins::kStoreInArrayLiteralIC_Slow_NoTransitionHandleCOW:
case Builtins::kKeyedStoreIC_Slow_NoTransitionHandleCOW:
case Builtins::kStoreFastElementIC_NoTransitionHandleCOW:
case Builtins::kElementsTransitionAndStore_NoTransitionHandleCOW:
return STORE_NO_TRANSITION_HANDLE_COW;
return STORE_HANDLE_COW;
default:
UNREACHABLE();
}

View File

@ -1504,55 +1504,20 @@ enum KeyedAccessLoadMode {
enum KeyedAccessStoreMode {
STANDARD_STORE,
STORE_TRANSITION_TO_OBJECT,
STORE_TRANSITION_TO_DOUBLE,
STORE_AND_GROW_NO_TRANSITION_HANDLE_COW,
STORE_AND_GROW_TRANSITION_TO_OBJECT,
STORE_AND_GROW_TRANSITION_TO_DOUBLE,
STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS,
STORE_NO_TRANSITION_HANDLE_COW
STORE_AND_GROW_HANDLE_COW,
STORE_IGNORE_OUT_OF_BOUNDS,
STORE_HANDLE_COW
};
enum MutableMode { MUTABLE, IMMUTABLE };
static inline bool IsTransitionStoreMode(KeyedAccessStoreMode store_mode) {
return store_mode == STORE_TRANSITION_TO_OBJECT ||
store_mode == STORE_TRANSITION_TO_DOUBLE ||
store_mode == STORE_AND_GROW_TRANSITION_TO_OBJECT ||
store_mode == STORE_AND_GROW_TRANSITION_TO_DOUBLE;
}
static inline bool IsCOWHandlingStoreMode(KeyedAccessStoreMode store_mode) {
return store_mode == STORE_NO_TRANSITION_HANDLE_COW ||
store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW;
}
static inline KeyedAccessStoreMode GetNonTransitioningStoreMode(
KeyedAccessStoreMode store_mode, bool receiver_was_cow) {
switch (store_mode) {
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_TRANSITION_TO_OBJECT:
case STORE_AND_GROW_TRANSITION_TO_DOUBLE:
store_mode = STORE_AND_GROW_NO_TRANSITION_HANDLE_COW;
break;
case STANDARD_STORE:
case STORE_TRANSITION_TO_OBJECT:
case STORE_TRANSITION_TO_DOUBLE:
store_mode =
receiver_was_cow ? STORE_NO_TRANSITION_HANDLE_COW : STANDARD_STORE;
break;
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_NO_TRANSITION_HANDLE_COW:
break;
}
DCHECK(!IsTransitionStoreMode(store_mode));
DCHECK_IMPLIES(receiver_was_cow, IsCOWHandlingStoreMode(store_mode));
return store_mode;
return store_mode == STORE_HANDLE_COW ||
store_mode == STORE_AND_GROW_HANDLE_COW;
}
static inline bool IsGrowStoreMode(KeyedAccessStoreMode store_mode) {
return store_mode >= STORE_AND_GROW_NO_TRANSITION_HANDLE_COW &&
store_mode <= STORE_AND_GROW_TRANSITION_TO_DOUBLE;
return store_mode == STORE_AND_GROW_HANDLE_COW;
}
enum IcCheckType { ELEMENT, PROPERTY };

View File

@ -73,17 +73,16 @@ const char* GetModifier(KeyedAccessLoadMode mode) {
const char* GetModifier(KeyedAccessStoreMode mode) {
switch (mode) {
case STORE_NO_TRANSITION_HANDLE_COW:
case STORE_HANDLE_COW:
return ".COW";
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case STORE_AND_GROW_HANDLE_COW:
return ".STORE+COW";
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
case STORE_IGNORE_OUT_OF_BOUNDS:
return ".IGNORE_OOB";
default:
break;
case STANDARD_STORE:
return "";
}
DCHECK(!IsCOWHandlingStoreMode(mode));
return IsGrowStoreMode(mode) ? ".GROW" : "";
UNREACHABLE();
}
} // namespace
@ -1767,13 +1766,12 @@ MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) {
void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
KeyedAccessStoreMode store_mode,
bool receiver_was_cow) {
TransitionMode transition_mode) {
MapHandles target_receiver_maps;
TargetMaps(&target_receiver_maps);
if (target_receiver_maps.empty()) {
Handle<Map> monomorphic_map =
ComputeTransitionedMap(receiver_map, store_mode);
store_mode = GetNonTransitioningStoreMode(store_mode, receiver_was_cow);
ComputeTransitionedMap(receiver_map, transition_mode);
Handle<Object> handler = StoreElementHandler(monomorphic_map, store_mode);
return ConfigureVectorState(Handle<Name>(), monomorphic_map, handler);
}
@ -1787,26 +1785,21 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
}
// 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;
old_store_mode = GetKeyedAccessStoreMode();
// transition to a different 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 = GetKeyedAccessStoreMode();
Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
if (state() == MONOMORPHIC) {
Handle<Map> transitioned_receiver_map = receiver_map;
if (IsTransitionStoreMode(store_mode)) {
transitioned_receiver_map =
ComputeTransitionedMap(receiver_map, store_mode);
}
Handle<Map> transitioned_receiver_map =
ComputeTransitionedMap(receiver_map, transition_mode);
if ((receiver_map.is_identical_to(previous_receiver_map) &&
IsTransitionStoreMode(store_mode)) ||
transition_mode != TransitionMode::kNoTransition) ||
IsTransitionOfMonomorphicTarget(*previous_receiver_map,
*transitioned_receiver_map)) {
// If the "old" and "new" maps are in the same elements map family, or
// if they at least come from the same origin for a transitioning store,
// stay MONOMORPHIC and use the map for the most generic ElementsKind.
store_mode = GetNonTransitioningStoreMode(store_mode, receiver_was_cow);
Handle<Object> handler =
StoreElementHandler(transitioned_receiver_map, store_mode);
ConfigureVectorState(Handle<Name>(), transitioned_receiver_map, handler);
@ -1814,9 +1807,8 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
}
if (receiver_map.is_identical_to(previous_receiver_map) &&
old_store_mode == STANDARD_STORE &&
(store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW ||
store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
store_mode == STORE_NO_TRANSITION_HANDLE_COW)) {
transition_mode == TransitionMode::kNoTransition &&
store_mode != STANDARD_STORE) {
// A "normal" IC that handles stores can switch to a version that can
// grow at the end of the array, handle OOB accesses or copy COW arrays
// and still stay MONOMORPHIC.
@ -1830,9 +1822,9 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
bool map_added =
AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map);
if (IsTransitionStoreMode(store_mode)) {
if (transition_mode != TransitionMode::kNoTransition) {
Handle<Map> transitioned_receiver_map =
ComputeTransitionedMap(receiver_map, store_mode);
ComputeTransitionedMap(receiver_map, transition_mode);
map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps,
transitioned_receiver_map);
}
@ -1850,7 +1842,6 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
// Make sure all polymorphic handlers have the same store mode, otherwise the
// megamorphic stub must be used.
store_mode = GetNonTransitioningStoreMode(store_mode, receiver_was_cow);
if (old_store_mode != STANDARD_STORE) {
if (store_mode == STANDARD_STORE) {
store_mode = old_store_mode;
@ -1896,28 +1887,21 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
}
Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
Handle<Map> map, KeyedAccessStoreMode store_mode) {
switch (store_mode) {
case STORE_TRANSITION_TO_OBJECT:
case STORE_AND_GROW_TRANSITION_TO_OBJECT: {
Handle<Map> map, TransitionMode transition_mode) {
switch (transition_mode) {
case TransitionMode::kTransitionToObject: {
ElementsKind kind = IsHoleyElementsKind(map->elements_kind())
? HOLEY_ELEMENTS
: PACKED_ELEMENTS;
return Map::TransitionElementsTo(isolate(), map, kind);
}
case STORE_TRANSITION_TO_DOUBLE:
case STORE_AND_GROW_TRANSITION_TO_DOUBLE: {
case TransitionMode::kTransitionToDouble: {
ElementsKind kind = IsHoleyElementsKind(map->elements_kind())
? HOLEY_DOUBLE_ELEMENTS
: PACKED_DOUBLE_ELEMENTS;
return Map::TransitionElementsTo(isolate(), map, kind);
}
case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
DCHECK(map->has_fixed_typed_array_elements());
V8_FALLTHROUGH;
case STORE_NO_TRANSITION_HANDLE_COW:
case STANDARD_STORE:
case STORE_AND_GROW_NO_TRANSITION_HANDLE_COW:
case TransitionMode::kNoTransition:
return map;
}
UNREACHABLE();
@ -1925,10 +1909,6 @@ Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
Handle<Object> KeyedStoreIC::StoreElementHandler(
Handle<Map> receiver_map, KeyedAccessStoreMode store_mode) {
DCHECK(store_mode == STANDARD_STORE ||
store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW ||
store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
DCHECK_IMPLIES(
receiver_map->DictionaryElementsInPrototypeChainOnly(isolate()),
IsStoreInArrayLiteralICKind(kind()));
@ -1980,11 +1960,6 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
void KeyedStoreIC::StoreElementPolymorphicHandlers(
MapHandles* receiver_maps, MaybeObjectHandles* handlers,
KeyedAccessStoreMode store_mode) {
DCHECK(store_mode == STANDARD_STORE ||
store_mode == STORE_AND_GROW_NO_TRANSITION_HANDLE_COW ||
store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
// Filter out deprecated maps to ensure their instances get migrated.
receiver_maps->erase(
std::remove_if(
@ -2046,8 +2021,7 @@ bool MayHaveTypedArrayInPrototypeChain(Handle<JSObject> object) {
return false;
}
KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, uint32_t index,
Handle<Object> value) {
KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, uint32_t index) {
bool oob_access = IsOutOfBoundsAccess(receiver, index);
// Don't consider this a growing store if the store would send the receiver to
// dictionary mode. Also make sure we don't consider this a growing store if
@ -2058,40 +2032,32 @@ KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, uint32_t index,
!receiver->WouldConvertToSlowElements(index) &&
!MayHaveTypedArrayInPrototypeChain(receiver);
if (allow_growth) {
// Handle growing array in stub if necessary.
if (receiver->HasSmiElements()) {
if (value->IsHeapNumber()) {
return STORE_AND_GROW_TRANSITION_TO_DOUBLE;
}
if (value->IsHeapObject()) {
return STORE_AND_GROW_TRANSITION_TO_OBJECT;
}
} else if (receiver->HasDoubleElements()) {
if (!value->IsSmi() && !value->IsHeapNumber()) {
return STORE_AND_GROW_TRANSITION_TO_OBJECT;
}
}
return STORE_AND_GROW_NO_TRANSITION_HANDLE_COW;
} else {
// Handle only in-bounds elements accesses.
if (receiver->HasSmiElements()) {
if (value->IsHeapNumber()) {
return STORE_TRANSITION_TO_DOUBLE;
} else if (value->IsHeapObject()) {
return STORE_TRANSITION_TO_OBJECT;
}
} else if (receiver->HasDoubleElements()) {
if (!value->IsSmi() && !value->IsHeapNumber()) {
return STORE_TRANSITION_TO_OBJECT;
}
}
if (!FLAG_trace_external_array_abuse &&
receiver->map()->has_fixed_typed_array_elements() && oob_access) {
return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
}
return receiver->elements()->IsCowArray() ? STORE_NO_TRANSITION_HANDLE_COW
: STANDARD_STORE;
return STORE_AND_GROW_HANDLE_COW;
}
if (!FLAG_trace_external_array_abuse &&
receiver->map()->has_fixed_typed_array_elements() && oob_access) {
return STORE_IGNORE_OUT_OF_BOUNDS;
}
return receiver->elements()->IsCowArray() ? STORE_HANDLE_COW : STANDARD_STORE;
}
TransitionMode GetTransitionMode(Handle<JSObject> receiver,
Handle<Object> value) {
// TODO(mythria): Also consider index and the current length and handle
// transitions to HOLEY.
if (receiver->HasSmiElements()) {
if (value->IsHeapNumber()) {
return TransitionMode::kTransitionToDouble;
}
if (value->IsHeapObject()) {
return TransitionMode::kTransitionToObject;
}
} else if (receiver->HasDoubleElements()) {
if (!value->IsSmi() && !value->IsHeapNumber()) {
return TransitionMode::kTransitionToObject;
}
}
return TransitionMode::kNoTransition;
}
} // namespace
@ -2154,6 +2120,7 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
bool is_arguments = false;
bool key_is_valid_index = false;
KeyedAccessStoreMode store_mode = STANDARD_STORE;
TransitionMode transition_mode = TransitionMode::kNoTransition;
if (use_ic && object->IsJSReceiver()) {
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
old_receiver_map = handle(receiver->map(), isolate());
@ -2170,15 +2137,13 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
if (key_is_valid_index) {
uint32_t index = static_cast<uint32_t>(Smi::ToInt(*key));
Handle<JSObject> receiver_object = Handle<JSObject>::cast(object);
store_mode = GetStoreMode(receiver_object, index, value);
store_mode = GetStoreMode(receiver_object, index);
transition_mode = GetTransitionMode(receiver_object, value);
}
}
}
DCHECK(store_handle.is_null());
bool receiver_was_cow =
object->IsJSArray() &&
Handle<JSArray>::cast(object)->elements()->IsCowArray();
ASSIGN_RETURN_ON_EXCEPTION(
isolate(), store_handle,
Runtime::SetObjectProperty(isolate(), object, key, value,
@ -2203,14 +2168,13 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
// (Runtime::SetObjectProperty).
if (old_receiver_map->elements_kind() ==
Handle<HeapObject>::cast(object)->map()->elements_kind()) {
store_mode =
GetNonTransitioningStoreMode(store_mode, receiver_was_cow);
transition_mode = TransitionMode::kNoTransition;
}
// We should go generic if receiver isn't a dictionary, but our
// prototype chain does have dictionary elements. This ensures that
// other non-dictionary receivers in the polymorphic case benefit
// from fast path keyed stores.
UpdateStoreElement(old_receiver_map, store_mode, receiver_was_cow);
UpdateStoreElement(old_receiver_map, store_mode, transition_mode);
} else {
set_slow_stub_reason("dictionary or proxy prototype");
}
@ -2259,19 +2223,20 @@ void StoreInArrayLiteralIC::Store(Handle<JSArray> array, Handle<Object> index,
// TODO(neis): Convert HeapNumber to Smi if possible?
KeyedAccessStoreMode store_mode = STANDARD_STORE;
TransitionMode transition_mode = TransitionMode::kNoTransition;
if (index->IsSmi()) {
DCHECK_GE(Smi::ToInt(*index), 0);
uint32_t index32 = static_cast<uint32_t>(Smi::ToInt(*index));
store_mode = GetStoreMode(array, index32, value);
store_mode = GetStoreMode(array, index32);
transition_mode = GetTransitionMode(array, value);
}
Handle<Map> old_array_map(array->map(), isolate());
bool array_was_cow = array->elements()->IsCowArray();
StoreOwnElement(isolate(), array, index, value);
if (index->IsSmi()) {
DCHECK(!old_array_map->is_abandoned_prototype_map());
UpdateStoreElement(old_array_map, store_mode, array_was_cow);
UpdateStoreElement(old_array_map, store_mode, transition_mode);
} else {
set_slow_stub_reason("index out of Smi range");
}

View File

@ -334,6 +334,12 @@ enum KeyedStoreCheckMap { kDontCheckMap, kCheckMap };
enum KeyedStoreIncrementLength { kDontIncrementLength, kIncrementLength };
enum class TransitionMode {
kNoTransition,
kTransitionToDouble,
kTransitionToObject
};
class KeyedStoreIC : public StoreIC {
public:
KeyedAccessStoreMode GetKeyedAccessStoreMode() {
@ -351,7 +357,7 @@ class KeyedStoreIC : public StoreIC {
protected:
void UpdateStoreElement(Handle<Map> receiver_map,
KeyedAccessStoreMode store_mode,
bool receiver_was_cow);
TransitionMode transition_mode);
Handle<Code> slow_stub() const override {
return BUILTIN_CODE(isolate(), KeyedStoreIC_Slow);
@ -359,7 +365,7 @@ class KeyedStoreIC : public StoreIC {
private:
Handle<Map> ComputeTransitionedMap(Handle<Map> map,
KeyedAccessStoreMode store_mode);
TransitionMode transition_mode);
Handle<Object> StoreElementHandler(Handle<Map> receiver_map,
KeyedAccessStoreMode store_mode);