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
This commit is contained in:
parent
2f36a51872
commit
e086746ed7
437
src/ic.cc
437
src/ic.cc
@ -830,9 +830,9 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* IC::Load(State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
MaybeObject* LoadIC::Load(State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> 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<Code> 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<Code> 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<Map> receiver_map = receiver_maps->at(i);
|
||||
Handle<Code> cached_stub = ComputeMonomorphicStubWithoutMapCheck(
|
||||
receiver_map, strict_mode, growth_mode);
|
||||
handler_ics.Add(cached_stub);
|
||||
}
|
||||
KeyedLoadStubCompiler compiler(isolate());
|
||||
Handle<Code> 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<Object> TryConvertKey(Handle<Object> 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<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
|
||||
}
|
||||
|
||||
|
||||
static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps,
|
||||
Handle<Map> 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<Code> 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<Map>(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> object(info->target_object());
|
||||
ASSERT(object->IsMap());
|
||||
AddOneReceiverMapIfMissing(result, Handle<Map>::cast(object));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MEGAMORPHIC:
|
||||
case GENERIC:
|
||||
break;
|
||||
case UNINITIALIZED:
|
||||
case PREMONOMORPHIC:
|
||||
case MONOMORPHIC_PROTOTYPE_FAILURE:
|
||||
case DEBUG_STUB:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedLoadIC::LoadElementStub(Handle<JSObject> 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<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.
|
||||
return isolate()->stub_cache()->ComputeKeyedLoadElement(receiver_map);
|
||||
}
|
||||
|
||||
if (target() == *string_stub()) {
|
||||
target_receiver_maps.Add(isolate()->factory()->string_map());
|
||||
} else {
|
||||
GetReceiverMapsForStub(Handle<Code>(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> object,
|
||||
Handle<Object> key,
|
||||
@ -1151,7 +1224,7 @@ MaybeObject* KeyedLoadIC::Load(State state,
|
||||
key = TryConvertKey(key, isolate());
|
||||
|
||||
if (key->IsSymbol()) {
|
||||
return IC::Load(state, object, Handle<String>::cast(key));
|
||||
return LoadIC::Load(state, object, Handle<String>::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<JSObject> receiver,
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* IC::Store(State state,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Object> object,
|
||||
Handle<String> name,
|
||||
Handle<Object> value,
|
||||
JSReceiver::StoreFromKeyed store_mode) {
|
||||
MaybeObject* StoreIC::Store(State state,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Object> object,
|
||||
Handle<String> name,
|
||||
Handle<Object> 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<Map> 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<Code> 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<Map>(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> object(info->target_object());
|
||||
ASSERT(object->IsMap());
|
||||
AddOneReceiverMapIfMissing(result, Handle<Map>::cast(object));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MEGAMORPHIC:
|
||||
case GENERIC:
|
||||
break;
|
||||
case UNINITIALIZED:
|
||||
case PREMONOMORPHIC:
|
||||
case MONOMORPHIC_PROTOTYPE_FAILURE:
|
||||
case DEBUG_STUB:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedIC::ComputeStub(Handle<JSObject> receiver,
|
||||
StubKind stub_kind,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Code> generic_stub) {
|
||||
Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> 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<Code> KeyedIC::ComputeStub(Handle<JSObject> 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<Map> receiver_map(receiver->map());
|
||||
Handle<Map> 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<Code>(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<Code>(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<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);
|
||||
}
|
||||
|
||||
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<Map> 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<Code> KeyedIC::ComputeStub(Handle<JSObject> receiver,
|
||||
grow_mode = ALLOW_JSARRAY_GROWTH;
|
||||
}
|
||||
|
||||
Handle<PolymorphicCodeCache> 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<Object> probe = cache->Lookup(&target_receiver_maps, flags);
|
||||
if (probe->IsCode()) return Handle<Code>::cast(probe);
|
||||
|
||||
Handle<Code> stub =
|
||||
ComputePolymorphicStub(&target_receiver_maps, strict_mode, grow_mode);
|
||||
PolymorphicCodeCache::Update(cache, &target_receiver_maps, flags, stub);
|
||||
return stub;
|
||||
return isolate()->stub_cache()->ComputeStoreElementPolymorphic(
|
||||
&target_receiver_maps, grow_mode, strict_mode);
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
|
||||
Handle<Map> 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<Code> KeyedIC::ComputeMonomorphicStub(Handle<Map> receiver_map,
|
||||
StubKind stub_kind,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Code> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<Map> KeyedIC::ComputeTransitionedMap(Handle<JSObject> receiver,
|
||||
StubKind stub_kind) {
|
||||
Handle<Map> KeyedStoreIC::ComputeTransitionedMap(Handle<JSObject> 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<Map> KeyedIC::ComputeTransitionedMap(Handle<JSObject> receiver,
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedStoreIC::GetElementStubWithoutMapCheck(
|
||||
bool is_js_array,
|
||||
ElementsKind elements_kind,
|
||||
KeyedAccessGrowMode grow_mode) {
|
||||
return KeyedStoreElementStub(is_js_array, elements_kind, grow_mode).GetCode();
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> 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<Map> receiver_map(receiver_maps->at(i));
|
||||
Handle<Code> cached_stub;
|
||||
Handle<Map> 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> 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<JSObject> receiver,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value) {
|
||||
KeyedStoreIC::StubKind KeyedStoreIC::GetStubKind(Handle<JSObject> receiver,
|
||||
Handle<Object> key,
|
||||
Handle<Object> 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<String> name = Handle<String>::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");
|
||||
|
276
src/ic.h
276
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> object,
|
||||
Handle<String> name);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* Store(
|
||||
State state,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Object> object,
|
||||
Handle<String> name,
|
||||
Handle<Object> value,
|
||||
JSReceiver::StoreFromKeyed store_mode =
|
||||
JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED);
|
||||
|
||||
protected:
|
||||
virtual Handle<Code> pre_monomorphic_stub() {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Handle<Code> megamorphic_stub() {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Handle<Code> megamorphic_stub_strict() {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Handle<Code> generic_stub() const {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Code::Kind kind() const {
|
||||
UNREACHABLE();
|
||||
return Code::STUB;
|
||||
}
|
||||
virtual Handle<Code> global_proxy_stub() {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
virtual Handle<Code> global_proxy_stub_strict() {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
virtual void UpdateLoadCaches(LookupResult* lookup,
|
||||
State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
virtual void UpdateStoreCaches(LookupResult* lookup,
|
||||
State state,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<JSObject> receiver,
|
||||
Handle<String> name,
|
||||
Handle<Object> 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> object,
|
||||
Handle<String> name);
|
||||
|
||||
protected:
|
||||
virtual Code::Kind kind() const { return Code::LOAD_IC; }
|
||||
|
||||
virtual Handle<Code> generic_stub() const {
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
virtual Handle<Code> 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<StubKind>(static_cast<int>(stub_kind) +
|
||||
kGrowICDelta);
|
||||
}
|
||||
return stub_kind;
|
||||
}
|
||||
|
||||
virtual Handle<Code> GetElementStubWithoutMapCheck(
|
||||
bool is_js_array,
|
||||
ElementsKind elements_kind,
|
||||
KeyedAccessGrowMode grow_mode) = 0;
|
||||
|
||||
protected:
|
||||
virtual Handle<Code> string_stub() {
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
Handle<Code> ComputeStub(Handle<JSObject> receiver,
|
||||
StubKind stub_kind,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Code> default_stub);
|
||||
|
||||
virtual Handle<Code> ComputePolymorphicStub(
|
||||
MapHandleList* receiver_maps,
|
||||
StrictModeFlag strict_mode,
|
||||
KeyedAccessGrowMode grow_mode) = 0;
|
||||
|
||||
Handle<Code> ComputeMonomorphicStubWithoutMapCheck(
|
||||
Handle<Map> receiver_map,
|
||||
StrictModeFlag strict_mode,
|
||||
KeyedAccessGrowMode grow_mode);
|
||||
|
||||
private:
|
||||
void GetReceiverMapsForStub(Handle<Code> stub, MapHandleList* result);
|
||||
|
||||
Handle<Code> ComputeMonomorphicStub(Handle<Map> receiver_map,
|
||||
StubKind stub_kind,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<Code> default_stub);
|
||||
|
||||
Handle<Map> ComputeTransitionedMap(Handle<JSObject> 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<Code> GetElementStubWithoutMapCheck(
|
||||
bool is_js_array,
|
||||
ElementsKind elements_kind,
|
||||
KeyedAccessGrowMode grow_mode);
|
||||
|
||||
protected:
|
||||
virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; }
|
||||
|
||||
Handle<Code> LoadElementStub(Handle<JSObject> receiver);
|
||||
|
||||
virtual Handle<Code> megamorphic_stub() {
|
||||
return isolate()->builtins()->KeyedLoadIC_Generic();
|
||||
}
|
||||
@ -578,14 +424,6 @@ class KeyedLoadIC: public KeyedIC {
|
||||
return isolate()->builtins()->KeyedLoadIC_Generic();
|
||||
}
|
||||
|
||||
virtual Handle<Code> ComputePolymorphicStub(MapHandleList* receiver_maps,
|
||||
StrictModeFlag strict_mode,
|
||||
KeyedAccessGrowMode grow_mode);
|
||||
|
||||
virtual Handle<Code> 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<Code> non_strict_arguments_stub() {
|
||||
return isolate()->builtins()->KeyedLoadIC_NonStrictArguments();
|
||||
}
|
||||
Handle<Code> 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> object,
|
||||
Handle<String> name,
|
||||
Handle<Object> value,
|
||||
JSReceiver::StoreFromKeyed store_mode =
|
||||
JSReceiver::CERTAINLY_NOT_STORE_FROM_KEYED);
|
||||
|
||||
protected:
|
||||
virtual Code::Kind kind() const { return Code::STORE_IC; }
|
||||
virtual Handle<Code> 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<StubKind>(static_cast<int>(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<Code> GetElementStubWithoutMapCheck(
|
||||
bool is_js_array,
|
||||
ElementsKind elements_kind,
|
||||
KeyedAccessGrowMode grow_mode);
|
||||
|
||||
protected:
|
||||
virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; }
|
||||
|
||||
virtual Handle<Code> 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<Code> StoreElementStub(Handle<JSObject> 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<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;
|
||||
}
|
||||
|
||||
Handle<Map> ComputeTransitionedMap(Handle<JSObject> receiver,
|
||||
StubKind stub_kind);
|
||||
|
||||
friend class IC;
|
||||
};
|
||||
|
||||
|
@ -371,69 +371,48 @@ Handle<Code> StubCache::ComputeStoreField(Handle<String> name,
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> StubCache::ComputeKeyedLoadOrStoreElement(
|
||||
Handle<Map> receiver_map,
|
||||
KeyedIC::StubKind stub_kind,
|
||||
StrictModeFlag strict_mode) {
|
||||
KeyedAccessGrowMode grow_mode =
|
||||
KeyedIC::GetGrowModeFromStubKind(stub_kind);
|
||||
Code::ExtraICState extra_state =
|
||||
Code::ComputeExtraICState(grow_mode, strict_mode);
|
||||
Handle<Code> StubCache::ComputeKeyedLoadElement(Handle<Map> receiver_map) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(
|
||||
stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC
|
||||
: Code::KEYED_STORE_IC,
|
||||
Code::NORMAL,
|
||||
extra_state);
|
||||
Handle<String> 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::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, Code::NORMAL);
|
||||
Handle<String> name =
|
||||
isolate()->factory()->KeyedLoadElementMonomorphic_symbol();
|
||||
|
||||
Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate_);
|
||||
if (probe->IsCode()) return Handle<Code>::cast(probe);
|
||||
|
||||
Handle<Code> 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;
|
||||
}
|
||||
KeyedLoadStubCompiler compiler(isolate());
|
||||
Handle<Code> code = compiler.CompileLoadElement(receiver_map);
|
||||
|
||||
ASSERT(!code.is_null());
|
||||
PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0));
|
||||
Map::UpdateCodeCache(receiver_map, name, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Handle<Code> StubCache::ComputeKeyedStoreElement(
|
||||
Handle<Map> receiver_map,
|
||||
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(
|
||||
Code::KEYED_STORE_IC, Code::NORMAL, extra_state);
|
||||
|
||||
ASSERT(stub_kind == KeyedStoreIC::STORE_NO_TRANSITION ||
|
||||
stub_kind == KeyedStoreIC::STORE_AND_GROW_NO_TRANSITION);
|
||||
|
||||
Handle<String> name = stub_kind == KeyedStoreIC::STORE_NO_TRANSITION
|
||||
? isolate()->factory()->KeyedStoreElementMonomorphic_symbol()
|
||||
: isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol();
|
||||
|
||||
Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate_);
|
||||
if (probe->IsCode()) return Handle<Code>::cast(probe);
|
||||
|
||||
KeyedStoreStubCompiler compiler(isolate(), strict_mode, grow_mode);
|
||||
Handle<Code> code = compiler.CompileStoreElement(receiver_map);
|
||||
|
||||
PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0));
|
||||
Map::UpdateCodeCache(receiver_map, name, code);
|
||||
return code;
|
||||
}
|
||||
@ -851,6 +830,41 @@ Handle<Code> StubCache::ComputeCallMiss(int argc,
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> StubCache::ComputeLoadElementPolymorphic(
|
||||
MapHandleList* receiver_maps) {
|
||||
Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC);
|
||||
Handle<PolymorphicCodeCache> cache =
|
||||
isolate_->factory()->polymorphic_code_cache();
|
||||
Handle<Object> probe = cache->Lookup(receiver_maps, flags);
|
||||
if (probe->IsCode()) return Handle<Code>::cast(probe);
|
||||
|
||||
KeyedLoadStubCompiler compiler(isolate_);
|
||||
Handle<Code> code = compiler.CompileLoadElementPolymorphic(receiver_maps);
|
||||
PolymorphicCodeCache::Update(cache, receiver_maps, flags, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> StubCache::ComputeStoreElementPolymorphic(
|
||||
MapHandleList* receiver_maps,
|
||||
KeyedAccessGrowMode grow_mode,
|
||||
StrictModeFlag strict_mode) {
|
||||
Handle<PolymorphicCodeCache> 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<Object> probe = cache->Lookup(receiver_maps, flags);
|
||||
if (probe->IsCode()) return Handle<Code>::cast(probe);
|
||||
|
||||
KeyedStoreStubCompiler compiler(isolate_, strict_mode, grow_mode);
|
||||
Handle<Code> code = compiler.CompileStoreElementPolymorphic(receiver_maps);
|
||||
PolymorphicCodeCache::Update(cache, receiver_maps, flags, code);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
Handle<Code> StubCache::ComputeCallDebugBreak(int argc,
|
||||
Code::Kind kind) {
|
||||
@ -1366,6 +1380,40 @@ Handle<Code> KeyedLoadStubCompiler::GetCode(Code::StubType type,
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> KeyedLoadStubCompiler::CompileLoadElementPolymorphic(
|
||||
MapHandleList* receiver_maps) {
|
||||
CodeHandleList handler_ics(receiver_maps->length());
|
||||
for (int i = 0; i < receiver_maps->length(); ++i) {
|
||||
Handle<Map> receiver_map = receiver_maps->at(i);
|
||||
Handle<Code> 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> 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<Code> StoreStubCompiler::GetCode(Code::StubType type,
|
||||
Handle<String> name) {
|
||||
Code::Flags flags =
|
||||
@ -1391,6 +1439,50 @@ Handle<Code> KeyedStoreStubCompiler::GetCode(Code::StubType type,
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> 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<Map> receiver_map(receiver_maps->at(i));
|
||||
Handle<Code> cached_stub;
|
||||
Handle<Map> 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> 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);
|
||||
|
@ -172,9 +172,12 @@ class StubCache {
|
||||
Handle<Map> transition,
|
||||
StrictModeFlag strict_mode);
|
||||
|
||||
Handle<Code> ComputeKeyedLoadOrStoreElement(Handle<Map> receiver_map,
|
||||
KeyedIC::StubKind stub_kind,
|
||||
StrictModeFlag strict_mode);
|
||||
Handle<Code> ComputeKeyedLoadElement(Handle<Map> receiver_map);
|
||||
|
||||
Handle<Code> ComputeKeyedStoreElement(Handle<Map> 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<Code> ComputeLoadElementPolymorphic(MapHandleList* receiver_maps);
|
||||
Handle<Code> 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<Code> CompileLoadPolymorphic(MapHandleList* receiver_maps,
|
||||
CodeHandleList* handler_ics);
|
||||
|
||||
Handle<Code> 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<Code> CompileStoreElementPolymorphic(MapHandleList* receiver_maps);
|
||||
|
||||
static void GenerateStoreFastElement(MacroAssembler* masm,
|
||||
bool is_js_array,
|
||||
ElementsKind element_kind,
|
||||
|
Loading…
Reference in New Issue
Block a user