Handle<Code> stub =
KeyedStoreElementStub(is_js_array,
elements_kind,
- grow_mode_).GetCode(isolate());
+ store_mode_).GetCode(isolate());
__ DispatchMap(r2, r3, receiver_map, stub, DO_SMI_CHECK);
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
}
// Compare smis.
__ cmp(key_reg, scratch);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
__ b(hs, &grow);
} else {
__ b(hs, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Grow the array by a single element if possible.
__ bind(&grow);
void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
MacroAssembler* masm,
bool is_js_array,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
// Compare smis, unsigned compare catches both negative and out-of-bound
// indexes.
__ cmp(key_reg, scratch1);
- if (grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (IsGrowStoreMode(store_mode)) {
__ b(hs, &grow);
} else {
__ b(hs, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Grow the array by a single element if possible.
__ bind(&grow);
KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
is_js_array_,
elements_kind_,
- grow_mode_);
+ store_mode_);
}
break;
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
is_js_array_,
- grow_mode_);
+ store_mode_);
break;
case EXTERNAL_BYTE_ELEMENTS:
case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
is_jsarray_,
to_,
- grow_mode_);
+ store_mode_);
} else if (IsFastSmiElementsKind(from_) &&
IsFastDoubleElementsKind(to_)) {
ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, &fail);
KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
is_jsarray_,
- grow_mode_);
+ store_mode_);
} else if (IsFastDoubleElementsKind(from_)) {
ASSERT(to_ == FAST_HOLEY_DOUBLE_ELEMENTS);
ElementsTransitionGenerator::
public:
KeyedStoreElementStub(bool is_js_array,
ElementsKind elements_kind,
- KeyedAccessGrowMode grow_mode)
+ KeyedAccessStoreMode store_mode)
: is_js_array_(is_js_array),
elements_kind_(elements_kind),
- grow_mode_(grow_mode),
+ store_mode_(store_mode),
fp_registers_(CanUseFPRegisters()) { }
Major MajorKey() { return KeyedStoreElement; }
int MinorKey() {
return ElementsKindBits::encode(elements_kind_) |
IsJSArrayBits::encode(is_js_array_) |
- GrowModeBits::encode(grow_mode_) |
+ StoreModeBits::encode(store_mode_) |
FPRegisters::encode(fp_registers_);
}
void Generate(MacroAssembler* masm);
private:
- class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
- class GrowModeBits: public BitField<KeyedAccessGrowMode, 8, 1> {};
- class IsJSArrayBits: public BitField<bool, 9, 1> {};
- class FPRegisters: public BitField<bool, 10, 1> {};
+ class ElementsKindBits: public BitField<ElementsKind, 0, 8> {};
+ class StoreModeBits: public BitField<KeyedAccessStoreMode, 8, 4> {};
+ class IsJSArrayBits: public BitField<bool, 12, 1> {};
+ class FPRegisters: public BitField<bool, 13, 1> {};
bool is_js_array_;
ElementsKind elements_kind_;
- KeyedAccessGrowMode grow_mode_;
+ KeyedAccessStoreMode store_mode_;
bool fp_registers_;
DISALLOW_COPY_AND_ASSIGN(KeyedStoreElementStub);
ElementsKind to,
bool is_jsarray,
StrictModeFlag strict_mode,
- KeyedAccessGrowMode grow_mode)
+ KeyedAccessStoreMode store_mode)
: from_(from),
to_(to),
is_jsarray_(is_jsarray),
strict_mode_(strict_mode),
- grow_mode_(grow_mode) {}
+ store_mode_(store_mode) {}
private:
- class FromBits: public BitField<ElementsKind, 0, 8> {};
- class ToBits: public BitField<ElementsKind, 8, 8> {};
- class IsJSArrayBits: public BitField<bool, 16, 1> {};
- class StrictModeBits: public BitField<StrictModeFlag, 17, 1> {};
- class GrowModeBits: public BitField<KeyedAccessGrowMode, 18, 1> {};
+ class FromBits: public BitField<ElementsKind, 0, 8> {};
+ class ToBits: public BitField<ElementsKind, 8, 8> {};
+ class IsJSArrayBits: public BitField<bool, 16, 1> {};
+ class StrictModeBits: public BitField<StrictModeFlag, 17, 1> {};
+ class StoreModeBits: public BitField<KeyedAccessStoreMode, 18, 4> {};
Major MajorKey() { return ElementsTransitionAndStore; }
int MinorKey() {
ToBits::encode(to_) |
IsJSArrayBits::encode(is_jsarray_) |
StrictModeBits::encode(strict_mode_) |
- GrowModeBits::encode(grow_mode_);
+ StoreModeBits::encode(store_mode_);
}
void Generate(MacroAssembler* masm);
ElementsKind to_;
bool is_jsarray_;
StrictModeFlag strict_mode_;
- KeyedAccessGrowMode grow_mode_;
+ KeyedAccessStoreMode store_mode_;
DISALLOW_COPY_AND_ASSIGN(ElementsTransitionAndStoreStub);
};
"KeyedLoadElementMonomorphic") \
V(KeyedStoreElementMonomorphic_string, \
"KeyedStoreElementMonomorphic") \
- V(KeyedStoreAndGrowElementMonomorphic_string, \
- "KeyedStoreAndGrowElementMonomorphic") \
V(stack_overflow_string, "kStackOverflowBoilerplate") \
V(illegal_access_string, "illegal access") \
V(out_of_memory_string, "out-of-memory") \
V(infinity_string, "Infinity") \
V(minus_infinity_string, "-Infinity") \
V(hidden_stack_trace_string, "v8::hidden_stack_trace") \
- V(query_colon_string, "(?:)") \
+ V(query_colon_string, "(?:)")
// Forward declarations.
class GCTracer;
Handle<Code> stub =
KeyedStoreElementStub(is_jsarray,
elements_kind,
- grow_mode_).GetCode(isolate());
+ store_mode_).GetCode(isolate());
__ DispatchMap(edx, receiver_map, stub, DO_SMI_CHECK);
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
if (is_js_array) {
// Check that the key is within bounds.
__ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
- if (grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (IsGrowStoreMode(store_mode)) {
__ j(above_equal, &grow);
} else {
__ j(above_equal, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Handle transition requiring the array to grow.
__ bind(&grow);
void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
MacroAssembler* masm,
bool is_js_array,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
if (is_js_array) {
// Check that the key is within bounds.
__ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // smis.
- if (grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (IsGrowStoreMode(store_mode)) {
__ j(above_equal, &grow);
} else {
__ j(above_equal, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Handle transition requiring the array to grow.
__ bind(&grow);
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,
}
}
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");
}
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.
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);
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) {
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:
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());
}
}
-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()) {
}
}
}
- 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;
+ }
}
}
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 {
class KeyedStoreIC: public StoreIC {
public:
- 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<StubKind>(static_cast<int>(stub_kind) +
- kGrowICDelta);
- }
- return stub_kind;
- }
-
explicit KeyedStoreIC(Isolate* isolate) : StoreIC(isolate) {
ASSERT(target()->is_keyed_store_stub());
}
}
Handle<Code> StoreElementStub(Handle<JSObject> receiver,
- StubKind stub_kind,
+ KeyedAccessStoreMode store_mode,
StrictModeFlag strict_mode);
private:
static void Clear(Address address, Code* target);
- StubKind GetStubKind(Handle<JSObject> receiver,
- Handle<Object> key,
- Handle<Object> 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;
- }
+ KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver,
+ Handle<Object> key,
+ Handle<Object> value);
Handle<Map> ComputeTransitionedMap(Handle<JSObject> receiver,
- StubKind stub_kind);
+ KeyedAccessStoreMode store_mode);
friend class IC;
};
enum InlinedSmiCheck { ENABLE_INLINED_SMI_CHECK, DISABLE_INLINED_SMI_CHECK };
void PatchInlinedSmiCode(Address address, InlinedSmiCheck check);
-DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss);
DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure);
} } // namespace v8::internal
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- a0 : value
// -- a1 : key
void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
MacroAssembler* masm,
bool is_js_array,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- a0 : value
// -- a1 : key
ALLOW_ELEMENT_TRANSITION_MAPS
};
-enum KeyedAccessGrowMode {
- DO_NOT_ALLOW_JSARRAY_GROWTH,
- ALLOW_JSARRAY_GROWTH
-};
+enum KeyedAccessStoreMode {
+ STANDARD_STORE,
+ 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,
+ STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS,
+ STORE_NO_TRANSITION_HANDLE_COW
+};
+
+
+static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION -
+ STANDARD_STORE;
+STATIC_ASSERT(STANDARD_STORE == 0);
+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 KeyedAccessStoreMode GetGrowStoreMode(
+ KeyedAccessStoreMode store_mode) {
+ if (store_mode < STORE_AND_GROW_NO_TRANSITION) {
+ store_mode = static_cast<KeyedAccessStoreMode>(
+ static_cast<int>(store_mode) + kGrowICDelta);
+ }
+ return store_mode;
+}
+
+
+static inline bool IsTransitionStoreMode(KeyedAccessStoreMode store_mode) {
+ return store_mode > STANDARD_STORE &&
+ store_mode <= STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT &&
+ store_mode != STORE_AND_GROW_NO_TRANSITION;
+}
+
+
+static inline KeyedAccessStoreMode GetNonTransitioningStoreMode(
+ KeyedAccessStoreMode store_mode) {
+ if (store_mode >= STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
+ return store_mode;
+ }
+ if (store_mode >= STORE_AND_GROW_NO_TRANSITION) {
+ return STORE_AND_GROW_NO_TRANSITION;
+ }
+ return STANDARD_STORE;
+}
+
+
+static inline bool IsGrowStoreMode(KeyedAccessStoreMode store_mode) {
+ return store_mode >= STORE_AND_GROW_NO_TRANSITION &&
+ store_mode <= STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
+}
+
// Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
void FindAllCode(CodeHandleList* code_list, int length);
class ExtraICStateStrictMode: public BitField<StrictModeFlag, 0, 1> {};
- class ExtraICStateKeyedAccessGrowMode:
- public BitField<KeyedAccessGrowMode, 1, 1> {}; // NOLINT
-
- static const int kExtraICStateGrowModeShift = 1;
+ class ExtraICStateKeyedAccessStoreMode:
+ public BitField<KeyedAccessStoreMode, 1, 4> {}; // NOLINT
static inline StrictModeFlag GetStrictMode(ExtraICState extra_ic_state) {
return ExtraICStateStrictMode::decode(extra_ic_state);
}
- static inline KeyedAccessGrowMode GetKeyedAccessGrowMode(
+ static inline KeyedAccessStoreMode GetKeyedAccessStoreMode(
ExtraICState extra_ic_state) {
- return ExtraICStateKeyedAccessGrowMode::decode(extra_ic_state);
+ return ExtraICStateKeyedAccessStoreMode::decode(extra_ic_state);
}
static inline ExtraICState ComputeExtraICState(
- KeyedAccessGrowMode grow_mode,
+ KeyedAccessStoreMode store_mode,
StrictModeFlag strict_mode) {
- return ExtraICStateKeyedAccessGrowMode::encode(grow_mode) |
+ return ExtraICStateKeyedAccessStoreMode::encode(store_mode) |
ExtraICStateStrictMode::encode(strict_mode);
}
class TypeField: public BitField<StubType, 3, 3> {};
class CacheHolderField: public BitField<InlineCacheHolderFlag, 6, 1> {};
class KindField: public BitField<Kind, 7, 4> {};
- class ExtraICStateField: public BitField<ExtraICState, 11, 2> {};
- class IsPregeneratedField: public BitField<bool, 13, 1> {};
+ class ExtraICStateField: public BitField<ExtraICState, 11, 5> {};
+ class IsPregeneratedField: public BitField<bool, 16, 1> {};
// KindSpecificFlags1 layout (STUB and OPTIMIZED_FUNCTION)
static const int kStackSlotsFirstBit = 0;
class StackCheckTableOffsetField: public BitField<int, 0, 31> {};
// Signed field cannot be encoded using the BitField class.
- static const int kArgumentsCountShift = 14;
+ static const int kArgumentsCountShift = 17;
static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1);
// This constant should be encodable in an ARM instruction.
Handle<Code> StubCache::ComputeKeyedStoreElement(
Handle<Map> receiver_map,
- KeyedStoreIC::StubKind stub_kind,
StrictModeFlag strict_mode,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
Code::ExtraICState extra_state =
- Code::ComputeExtraICState(grow_mode, strict_mode);
+ Code::ComputeExtraICState(store_mode, strict_mode);
Code::Flags flags = Code::ComputeMonomorphicFlags(
Code::KEYED_STORE_IC, extra_state);
- ASSERT(stub_kind == KeyedStoreIC::STORE_NO_TRANSITION ||
- stub_kind == KeyedStoreIC::STORE_AND_GROW_NO_TRANSITION);
-
- Handle<Name> name = stub_kind == KeyedStoreIC::STORE_NO_TRANSITION
- ? isolate()->factory()->KeyedStoreElementMonomorphic_string()
- : isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_string();
+ ASSERT(store_mode == STANDARD_STORE ||
+ store_mode == STORE_AND_GROW_NO_TRANSITION);
+ Handle<String> name =
+ isolate()->factory()->KeyedStoreElementMonomorphic_string();
Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate_);
if (probe->IsCode()) return Handle<Code>::cast(probe);
- KeyedStoreStubCompiler compiler(isolate(), strict_mode, grow_mode);
+ KeyedStoreStubCompiler compiler(isolate(), strict_mode, store_mode);
Handle<Code> code = compiler.CompileStoreElement(receiver_map);
Map::UpdateCodeCache(receiver_map, name, code);
+ ASSERT(Code::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode);
return code;
}
isolate_);
if (probe->IsCode()) return Handle<Code>::cast(probe);
- KeyedStoreStubCompiler compiler(isolate(), strict_mode,
- DO_NOT_ALLOW_JSARRAY_GROWTH);
+ KeyedStoreStubCompiler compiler(isolate(), strict_mode, STANDARD_STORE);
Handle<Code> code =
compiler.CompileStoreField(receiver, field_index, transition, name);
JSObject::UpdateMapCodeCache(receiver, name, code);
Handle<Code> StubCache::ComputeStoreElementPolymorphic(
MapHandleList* receiver_maps,
- KeyedAccessGrowMode grow_mode,
+ KeyedAccessStoreMode store_mode,
StrictModeFlag strict_mode) {
+ ASSERT(store_mode == STANDARD_STORE ||
+ store_mode == STORE_AND_GROW_NO_TRANSITION);
Handle<PolymorphicCodeCache> cache =
isolate_->factory()->polymorphic_code_cache();
- Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode,
+ Code::ExtraICState extra_state = Code::ComputeExtraICState(store_mode,
strict_mode);
Code::Flags flags =
Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state);
Handle<Object> probe = cache->Lookup(receiver_maps, flags);
if (probe->IsCode()) return Handle<Code>::cast(probe);
- KeyedStoreStubCompiler compiler(isolate_, strict_mode, grow_mode);
+ KeyedStoreStubCompiler compiler(isolate_, strict_mode, store_mode);
Handle<Code> code = compiler.CompileStoreElementPolymorphic(receiver_maps);
PolymorphicCodeCache::Update(cache, receiver_maps, flags, code);
return code;
Handle<Name> name,
InlineCacheState state) {
Code::ExtraICState extra_state =
- Code::ComputeExtraICState(grow_mode_, strict_mode_);
+ Code::ComputeExtraICState(store_mode_, strict_mode_);
Code::Flags flags =
Code::ComputeFlags(Code::KEYED_STORE_IC, state, extra_state, type);
Handle<Code> code = GetCodeWithFlags(flags, name);
transitioned_map->elements_kind(),
is_js_array,
strict_mode_,
- grow_mode_).GetCode(isolate());
+ store_mode_).GetCode(isolate());
} else {
cached_stub = KeyedStoreElementStub(
is_js_array,
elements_kind,
- grow_mode_).GetCode(isolate());
+ store_mode_).GetCode(isolate());
}
ASSERT(!cached_stub.is_null());
handlers.Add(cached_stub);
Handle<Code> ComputeKeyedLoadElement(Handle<Map> receiver_map);
Handle<Code> ComputeKeyedStoreElement(Handle<Map> receiver_map,
- KeyedStoreIC::StubKind stub_kind,
StrictModeFlag strict_mode,
- KeyedAccessGrowMode grow_mode);
+ KeyedAccessStoreMode store_mode);
// ---
Handle<Code> ComputeLoadElementPolymorphic(MapHandleList* receiver_maps);
Handle<Code> ComputeStoreElementPolymorphic(MapHandleList* receiver_maps,
- KeyedAccessGrowMode grow_mode,
+ KeyedAccessStoreMode store_mode,
StrictModeFlag strict_mode);
Handle<Code> ComputePolymorphicIC(MapHandleList* receiver_maps,
public:
KeyedStoreStubCompiler(Isolate* isolate,
StrictModeFlag strict_mode,
- KeyedAccessGrowMode grow_mode)
+ KeyedAccessStoreMode store_mode)
: StubCompiler(isolate),
strict_mode_(strict_mode),
- grow_mode_(grow_mode) { }
+ store_mode_(store_mode) { }
Handle<Code> CompileStoreField(Handle<JSObject> object,
int index,
static void GenerateStoreFastElement(MacroAssembler* masm,
bool is_js_array,
ElementsKind element_kind,
- KeyedAccessGrowMode grow_mode);
+ KeyedAccessStoreMode store_mode);
static void GenerateStoreFastDoubleElement(MacroAssembler* masm,
bool is_js_array,
- KeyedAccessGrowMode grow_mode);
+ KeyedAccessStoreMode store_mode);
static void GenerateStoreExternalArray(MacroAssembler* masm,
ElementsKind elements_kind);
InlineCacheState state = MONOMORPHIC);
StrictModeFlag strict_mode_;
- KeyedAccessGrowMode grow_mode_;
+ KeyedAccessStoreMode store_mode_;
};
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
- bool allow_growth =
- Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
- ALLOW_JSARRAY_GROWTH;
+ bool standard_store =
+ Code::GetKeyedAccessStoreMode(code->extra_ic_state()) ==
+ STANDARD_STORE;
bool preliminary_checks =
code->is_keyed_store_stub() &&
- !allow_growth &&
+ standard_store &&
code->ic_state() == MONOMORPHIC &&
Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL;
if (!preliminary_checks) return false;
Handle<Object> map_or_code = GetInfo(ast_id);
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
- bool allow_growth =
- Code::GetKeyedAccessGrowMode(code->extra_ic_state()) ==
- ALLOW_JSARRAY_GROWTH;
- return code->is_keyed_store_stub() && !allow_growth &&
+ bool standard_store =
+ Code::GetKeyedAccessStoreMode(code->extra_ic_state()) ==
+ STANDARD_STORE;
+ return code->is_keyed_store_stub() && standard_store &&
code->ic_state() == POLYMORPHIC;
}
return false;
}
+KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
+ TypeFeedbackId ast_id) {
+ Handle<Object> map_or_code = GetInfo(ast_id);
+ if (map_or_code->IsCode()) {
+ Handle<Code> code = Handle<Code>::cast(map_or_code);
+ if (code->kind() == Code::KEYED_STORE_IC) {
+ return Code::GetKeyedAccessStoreMode(code->extra_ic_state());
+ }
+ }
+ return STANDARD_STORE;
+}
+
+
void TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
Handle<String> name,
SmallMapList* types) {
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId ast_id);
+ KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
+
void LoadReceiverTypes(Property* expr,
Handle<String> name,
SmallMapList* types);
Handle<Code> stub =
KeyedStoreElementStub(is_js_array,
elements_kind,
- grow_mode_).GetCode(isolate());
+ store_mode_).GetCode(isolate());
__ DispatchMap(rdx, receiver_map, stub, DO_SMI_CHECK);
MacroAssembler* masm,
bool is_js_array,
ElementsKind elements_kind,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// Check that the key is within bounds.
if (is_js_array) {
__ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
- if (grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (IsGrowStoreMode(store_mode)) {
__ j(above_equal, &grow);
} else {
__ j(above_equal, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Grow the array by a single element if possible.
__ bind(&grow);
void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
MacroAssembler* masm,
bool is_js_array,
- KeyedAccessGrowMode grow_mode) {
+ KeyedAccessStoreMode store_mode) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// Check that the key is within bounds.
if (is_js_array) {
__ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset));
- if (grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (IsGrowStoreMode(store_mode)) {
__ j(above_equal, &grow);
} else {
__ j(above_equal, &miss_force_generic);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
- if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
+ if (is_js_array && IsGrowStoreMode(store_mode)) {
// Grow the array by a single element if possible.
__ bind(&grow);
// Flags: --allow-natives-syntax --smi-only-arrays --expose-gc
// Flags: --notrack_allocation_sites
+// Limit the number of stress runs to reduce polymorphism it defeats some of
+// they assumptions made about how elements transitions work because transition
+// stubs end up going generic. Flags: --stress-runs=2
+
// Test element kind of objects.
// Since --smi-only-arrays affects builtins, its default setting at compile
// time sticks if built with snapshot. If --smi-only-arrays is deactivated