Track elements_kind transitions in KeyedStoreICs.

Review URL: http://codereview.chromium.org/8166017

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9577 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f900fc9d
......@@ -3274,6 +3274,61 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) {
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreElementWithTransition(
Map* transitioned_map,
Map* untransitioned_map_1,
Map* untransitioned_map_2) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
// -- r2 : receiver
// -- lr : return address
// -- r3 : scratch
// -----------------------------------
// The order of map occurrences in the generated code below is important.
// Both IC code and Crankshaft rely on |transitioned_map| being the first
// map in the stub.
Code* notransition_stub;
ElementsKind elements_kind = transitioned_map->elements_kind();
bool is_js_array = transitioned_map->instance_type() == JS_ARRAY_TYPE;
MaybeObject* maybe_stub =
KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode();
if (!maybe_stub->To(&notransition_stub)) return maybe_stub;
Label just_store, miss;
__ JumpIfSmi(r2, &miss);
__ ldr(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
// r3: receiver->map().
__ mov(ip, Operand(Handle<Map>(transitioned_map)));
__ cmp(r3, ip);
__ b(eq, &just_store);
ASSERT_NE(untransitioned_map_1, NULL);
__ mov(ip, Operand(Handle<Map>(untransitioned_map_1)));
__ cmp(r3, ip);
Code* generic_stub = (strict_mode_ == kStrictMode)
? isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic_Strict)
: isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic);
__ Jump(Handle<Code>(generic_stub), RelocInfo::CODE_TARGET, eq);
if (untransitioned_map_2 != NULL) {
__ mov(ip, Operand(Handle<Map>(untransitioned_map_2)));
__ cmp(r3, ip);
__ Jump(Handle<Code>(generic_stub), RelocInfo::CODE_TARGET, eq);
}
__ bind(&miss);
Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic, RelocInfo::CODE_TARGET);
__ bind(&just_store);
__ Jump(Handle<Code>(notransition_stub), RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
MapList* receiver_maps,
CodeList* handler_ics) {
......@@ -4313,7 +4368,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
// -- r3 : scratch
// -- r4 : scratch (elements)
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
Register value_reg = r0;
Register key_reg = r1;
......@@ -4347,7 +4402,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
__ b(hs, &miss_force_generic);
if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
__ JumpIfNotSmi(value_reg, &miss_force_generic);
__ JumpIfNotSmi(value_reg, &transition_elements_kind);
__ add(scratch,
elements_reg,
Operand(FixedArray::kHeaderSize - kHeapObjectTag));
......@@ -4381,6 +4436,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
Handle<Code> ic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ Jump(ic, RelocInfo::CODE_TARGET);
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic_miss, RelocInfo::CODE_TARGET);
}
......@@ -4396,7 +4455,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
// -- r4 : scratch
// -- r5 : scratch
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
Register value_reg = r0;
Register key_reg = r1;
......@@ -4434,7 +4493,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
scratch2,
scratch3,
scratch4,
&miss_force_generic);
&transition_elements_kind);
__ Ret();
// Handle store cache miss, replacing the ic with the generic stub.
......@@ -4442,6 +4501,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
Handle<Code> ic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ Jump(ic, RelocInfo::CODE_TARGET);
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic_miss, RelocInfo::CODE_TARGET);
}
......
......@@ -2755,6 +2755,62 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) {
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreElementWithTransition(
Map* transitioned_map,
Map* untransitioned_map_1,
Map* untransitioned_map_2) {
// ----------- S t a t e -------------
// -- eax : value
// -- ecx : key
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
// The order of map occurrences in the generated code below is important.
// Both IC code and Crankshaft rely on |transitioned_map| being the first
// map in the stub.
Code* notransition_stub;
ElementsKind elements_kind = transitioned_map->elements_kind();
bool is_jsarray = transitioned_map->instance_type() == JS_ARRAY_TYPE;
MaybeObject* maybe_stub =
KeyedStoreElementStub(is_jsarray, elements_kind).TryGetCode();
if (!maybe_stub->To(&notransition_stub)) return maybe_stub;
Label just_store, miss;
__ JumpIfSmi(edx, &miss, Label::kNear);
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
// ebx: receiver->map().
__ cmp(ebx, Handle<Map>(transitioned_map));
__ j(equal, &just_store);
ASSERT_NE(untransitioned_map_1, NULL);
__ cmp(ebx, Handle<Map>(untransitioned_map_1));
// TODO(jkummerow): When we have specialized code to do the transition,
// call that code here, then jump to just_store when the call returns.
// <temporary: just use the generic stub>
Code* generic_stub = (strict_mode_ == kStrictMode)
? isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic_Strict)
: isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic);
__ j(equal, Handle<Code>(generic_stub));
// </temporary>
if (untransitioned_map_2 != NULL) {
__ cmp(ebx, Handle<Map>(untransitioned_map_2));
// <temporary: see above, same here>
__ j(equal, Handle<Code>(generic_stub));
// </temporary>
}
__ bind(&miss);
Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic, RelocInfo::CODE_TARGET);
__ bind(&just_store);
__ jmp(Handle<Code>(notransition_stub), RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
MapList* receiver_maps,
CodeList* handler_ics) {
......@@ -3906,7 +3962,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
// This stub is meant to be tail-jumped to, the receiver must already
// have been verified by the caller to not be a smi.
......@@ -3931,7 +3987,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
}
if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
__ JumpIfNotSmi(eax, &miss_force_generic);
__ JumpIfNotSmi(eax, &transition_elements_kind);
// ecx is a smi, use times_half_pointer_size instead of
// times_pointer_size
__ mov(FieldOperand(edi,
......@@ -3961,6 +4017,11 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
Handle<Code> ic_force_generic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
// Handle transition to other elements kinds without using the generic stub.
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
}
......@@ -3973,7 +4034,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
// This stub is meant to be tail-jumped to, the receiver must already
// have been verified by the caller to not be a smi.
......@@ -3999,7 +4060,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
ecx,
edx,
xmm0,
&miss_force_generic,
&transition_elements_kind,
true);
__ ret(0);
......@@ -4008,6 +4069,11 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
Handle<Code> ic_force_generic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
// Handle transition to other elements kinds without using the generic stub.
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
}
......
......@@ -1243,7 +1243,7 @@ MaybeObject* KeyedLoadIC::Load(State state,
stub = indexed_interceptor_stub();
} else if (key->IsSmi() && (target() != non_strict_arguments_stub())) {
MaybeObject* maybe_stub = ComputeStub(receiver,
false,
LOAD,
kNonStrictMode,
stub);
stub = maybe_stub->IsFailure() ?
......@@ -1593,14 +1593,15 @@ void KeyedIC::GetReceiverMapsForStub(Code* stub, MapList* result) {
MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
bool is_store,
StubKind stub_kind,
StrictModeFlag strict_mode,
Code* generic_stub) {
State ic_state = target()->ic_state();
if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
if ((ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) &&
!IsTransitionStubKind(stub_kind)) {
Code* monomorphic_stub;
MaybeObject* maybe_stub = ComputeMonomorphicStub(receiver,
is_store,
stub_kind,
strict_mode,
generic_stub);
if (!maybe_stub->To(&monomorphic_stub)) return maybe_stub;
......@@ -1619,8 +1620,17 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
// Determine the list of receiver maps that this call site has seen,
// adding the map that was just encountered.
MapList target_receiver_maps;
GetReceiverMapsForStub(target(), &target_receiver_maps);
if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver->map())) {
if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
target_receiver_maps.Add(receiver->map());
} else {
GetReceiverMapsForStub(target(), &target_receiver_maps);
}
Map* new_map = receiver->map();
if (IsTransitionStubKind(stub_kind)) {
MaybeObject* maybe_map = ComputeTransitionedMap(receiver, stub_kind);
if (!maybe_map->To(&new_map)) return maybe_map;
}
if (!AddOneReceiverMapIfMissing(&target_receiver_maps, new_map)) {
// If the miss wasn't due to an unseen map, a MEGAMORPHIC stub
// won't help, use the generic stub.
return generic_stub;
......@@ -1642,21 +1652,14 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
ASSERT(maybe_cached_stub->IsCode());
return Code::cast(maybe_cached_stub);
}
// Collect MONOMORPHIC stubs for all target_receiver_maps.
CodeList handler_ics(target_receiver_maps.length());
for (int i = 0; i < target_receiver_maps.length(); ++i) {
Map* receiver_map(target_receiver_maps.at(i));
MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
receiver_map, strict_mode);
Code* cached_stub;
if (!maybe_cached_stub->To(&cached_stub)) return maybe_cached_stub;
handler_ics.Add(cached_stub);
MaybeObject* maybe_stub = NULL;
if (IsTransitionStubKind(stub_kind)) {
maybe_stub = ComputePolymorphicStubWithTransition(
receiver, &target_receiver_maps, new_map, strict_mode);
} else {
maybe_stub = ComputePolymorphicStub(&target_receiver_maps, strict_mode);
}
// Build the MEGAMORPHIC stub.
Code* stub;
MaybeObject* maybe_stub = ConstructMegamorphicStub(&target_receiver_maps,
&handler_ics,
strict_mode);
if (!maybe_stub->To(&stub)) return maybe_stub;
MaybeObject* maybe_update = cache->Update(&target_receiver_maps, flags, stub);
if (maybe_update->IsFailure()) return maybe_update;
......@@ -1684,7 +1687,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
bool is_store,
StubKind stub_kind,
StrictModeFlag strict_mode,
Code* generic_stub) {
Code* result = NULL;
......@@ -1695,7 +1698,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
receiver->HasDictionaryElements()) {
MaybeObject* maybe_stub =
isolate()->stub_cache()->ComputeKeyedLoadOrStoreElement(
receiver, is_store, strict_mode);
receiver, stub_kind, strict_mode);
if (!maybe_stub->To(&result)) return maybe_stub;
} else {
result = generic_stub;
......@@ -1704,6 +1707,60 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
}
MaybeObject* KeyedIC::ComputePolymorphicStubWithTransition(
JSObject* receiver,
MapList* receiver_maps,
Map* new_map,
StrictModeFlag strict_mode) {
Map* existing_transitionable_map = NULL;
for (int i = 0; i < receiver_maps->length(); ++i) {
Map* map = receiver_maps->at(i);
if (map != receiver->map() && map != new_map) {
existing_transitionable_map = map;
break;
}
}
KeyedStoreStubCompiler compiler(strict_mode);
return compiler.CompileStoreElementWithTransition(
new_map,
receiver->map(),
existing_transitionable_map);
}
MaybeObject* KeyedIC::ComputePolymorphicStub(
MapList* receiver_maps,
StrictModeFlag strict_mode) {
// Collect MONOMORPHIC stubs for all target_receiver_maps.
CodeList handler_ics(receiver_maps->length());
for (int i = 0; i < receiver_maps->length(); ++i) {
Map* receiver_map(receiver_maps->at(i));
MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
receiver_map, strict_mode);
Code* cached_stub;
if (!maybe_cached_stub->To(&cached_stub)) return maybe_cached_stub;
handler_ics.Add(cached_stub);
}
// Build the MEGAMORPHIC stub.
return ConstructMegamorphicStub(receiver_maps, &handler_ics, strict_mode);
}
MaybeObject* KeyedIC::ComputeTransitionedMap(JSObject* receiver,
StubKind stub_kind) {
switch (stub_kind) {
case KeyedIC::STORE_TRANSITION_SMI_TO_OBJECT:
case KeyedIC::STORE_TRANSITION_DOUBLE_TO_OBJECT:
return receiver->GetElementsTransitionMap(FAST_ELEMENTS);
case KeyedIC::STORE_TRANSITION_SMI_TO_DOUBLE:
return receiver->GetElementsTransitionMap(FAST_DOUBLE_ELEMENTS);
default:
UNREACHABLE();
return NULL;
}
}
MaybeObject* KeyedStoreIC::GetElementStubWithoutMapCheck(
bool is_js_array,
ElementsKind elements_kind) {
......@@ -1786,9 +1843,21 @@ MaybeObject* KeyedStoreIC::Store(State state,
stub = non_strict_arguments_stub();
} else if (!force_generic) {
if (key->IsSmi() && (target() != non_strict_arguments_stub())) {
StubKind stub_kind = STORE_NO_TRANSITION;
if (receiver->GetElementsKind() == FAST_SMI_ONLY_ELEMENTS) {
if (value->IsHeapNumber()) {
stub_kind = STORE_TRANSITION_SMI_TO_DOUBLE;
} else if (value->IsHeapObject()) {
stub_kind = STORE_TRANSITION_SMI_TO_OBJECT;
}
} else if (receiver->GetElementsKind() == FAST_DOUBLE_ELEMENTS) {
if (!value->IsSmi() && !value->IsHeapNumber()) {
stub_kind = STORE_TRANSITION_DOUBLE_TO_OBJECT;
}
}
HandleScope scope(isolate());
MaybeObject* maybe_stub = ComputeStub(receiver,
true,
stub_kind,
strict_mode,
stub);
stub = maybe_stub->IsFailure() ?
......
......@@ -342,6 +342,13 @@ 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
};
explicit KeyedIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) {}
virtual ~KeyedIC() {}
......@@ -357,7 +364,7 @@ class KeyedIC: public IC {
virtual Code::Kind kind() const = 0;
MaybeObject* ComputeStub(JSObject* receiver,
bool is_store,
StubKind stub_kind,
StrictModeFlag strict_mode,
Code* default_stub);
......@@ -374,9 +381,23 @@ class KeyedIC: public IC {
StrictModeFlag strict_mode);
MaybeObject* ComputeMonomorphicStub(JSObject* receiver,
bool is_store,
StubKind stub_kind,
StrictModeFlag strict_mode,
Code* default_stub);
MaybeObject* ComputePolymorphicStubWithTransition(JSObject* receiver,
MapList* receiver_maps,
Map* new_map,
StrictModeFlag strict_mode);
MaybeObject* ComputePolymorphicStub(MapList* receiver_maps,
StrictModeFlag strict_mode);
MaybeObject* ComputeTransitionedMap(JSObject* receiver, StubKind stub_kind);
static bool IsTransitionStubKind(StubKind stub_kind) {
return stub_kind > STORE_NO_TRANSITION;
}
};
......
This diff is collapsed.
......@@ -13172,6 +13172,14 @@ ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(ExternalDoubleElements)
#undef ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
RUNTIME_FUNCTION(MaybeObject*, Runtime_HaveSameMap) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(JSObject, obj1, args[0]);
CONVERT_CHECKED(JSObject, obj2, args[1]);
return isolate->heap()->ToBoolean(obj1->map() == obj2->map());
}
// ----------------------------------------------------------------------------
// Implementation of Runtime
......
......@@ -370,6 +370,7 @@ namespace internal {
F(HasExternalUnsignedIntElements, 1, 1) \
F(HasExternalFloatElements, 1, 1) \
F(HasExternalDoubleElements, 1, 1) \
F(HaveSameMap, 2, 1) \
/* profiler */ \
F(ProfilerResume, 0, 1) \
F(ProfilerPause, 0, 1)
......
......@@ -497,38 +497,56 @@ MaybeObject* StubCache::ComputeStoreField(String* name,
MaybeObject* StubCache::ComputeKeyedLoadOrStoreElement(
JSObject* receiver,
bool is_store,
KeyedIC::StubKind stub_kind,
StrictModeFlag strict_mode) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(
is_store ? Code::KEYED_STORE_IC :
Code::KEYED_LOAD_IC,
stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC
: Code::KEYED_STORE_IC,
NORMAL,
strict_mode);
String* name = is_store
? isolate()->heap()->KeyedStoreElementMonomorphic_symbol()
: isolate()->heap()->KeyedLoadElementMonomorphic_symbol();
String* name = NULL;
switch (stub_kind) {
case KeyedIC::LOAD:
name = isolate()->heap()->KeyedLoadElementMonomorphic_symbol();
break;
case KeyedIC::STORE_NO_TRANSITION:
name = isolate()->heap()->KeyedStoreElementMonomorphic_symbol();
break;
default:
UNREACHABLE();
break;
}
Object* maybe_code = receiver->map()->FindInCodeCache(name, flags);
if (!maybe_code->IsUndefined()) return Code::cast(maybe_code);
MaybeObject* maybe_new_code = NULL;
Map* receiver_map = receiver->map();
if (is_store) {
KeyedStoreStubCompiler compiler(strict_mode);
maybe_new_code = compiler.CompileStoreElement(receiver_map);
} else {
KeyedLoadStubCompiler compiler;
maybe_new_code = compiler.CompileLoadElement(receiver_map);
MaybeObject* maybe_new_code = NULL;
switch (stub_kind) {
case KeyedIC::LOAD: {
KeyedLoadStubCompiler compiler;
maybe_new_code = compiler.CompileLoadElement(receiver_map);
break;
}
case KeyedIC::STORE_NO_TRANSITION: {
KeyedStoreStubCompiler compiler(strict_mode);
maybe_new_code = compiler.CompileStoreElement(receiver_map);
break;
}
default:
UNREACHABLE();
break;
}
Code* code;
Code* code = NULL;
if (!maybe_new_code->To(&code)) return maybe_new_code;
if (is_store) {
if (stub_kind == KeyedIC::LOAD) {
PROFILE(isolate_,
CodeCreateEvent(Logger::KEYED_STORE_IC_TAG,
CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG,
Code::cast(code), 0));
} else {
PROFILE(isolate_,
CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG,
CodeCreateEvent(Logger::KEYED_STORE_IC_TAG,
Code::cast(code), 0));
}
ASSERT(code->IsCode());
......
......@@ -30,6 +30,7 @@
#include "allocation.h"
#include "arguments.h"
#include "ic-inl.h"
#include "macro-assembler.h"
#include "objects.h"
#include "zone-inl.h"
......@@ -187,7 +188,7 @@ class StubCache {
MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreElement(
JSObject* receiver,
bool is_store,
KeyedIC::StubKind stub_kind,
StrictModeFlag strict_mode);
// ---
......@@ -699,6 +700,11 @@ class KeyedStoreStubCompiler: public StubCompiler {
MUST_USE_RESULT MaybeObject* CompileStoreElement(Map* receiver_map);
MUST_USE_RESULT MaybeObject* CompileStoreElementWithTransition(
Map* transitioned_map,
Map* untransitioned_map_1,
Map* untransitioned_map_2 = NULL);
MUST_USE_RESULT MaybeObject* CompileStoreMegamorphic(
MapList* receiver_maps,
CodeList* handler_ics);
......
......@@ -2603,6 +2603,56 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) {
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreElementWithTransition(
Map* transitioned_map,
Map* untransitioned_map_1,
Map* untransitioned_map_2) {
// ----------- S t a t e -------------
// -- rax : value
// -- rcx : key
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
// The order of map occurrences in the generated code below is important.
// Both IC code and Crankshaft rely on |transitioned_map| being the first
// map in the stub.
Code* notransition_stub;
ElementsKind elements_kind = transitioned_map->elements_kind();
bool is_js_array = transitioned_map->instance_type() == JS_ARRAY_TYPE;
MaybeObject* maybe_stub =
KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode();
if (!maybe_stub->To(&notransition_stub)) return maybe_stub;
Label just_store, miss;
__ JumpIfSmi(rdx, &miss, Label::kNear);
__ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
// rbx: receiver->map().
__ Cmp(rbx, Handle<Map>(transitioned_map));
__ j(equal, &just_store);
ASSERT_NE(untransitioned_map_1, NULL);
__ Cmp(rbx, Handle<Map>(untransitioned_map_1));
Code* generic_stub = (strict_mode_ == kStrictMode)
? isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic_Strict)
: isolate()->builtins()->builtin(Builtins::kKeyedStoreIC_Generic);
__ j(equal, Handle<Code>(generic_stub), RelocInfo::CODE_TARGET);
if (untransitioned_map_2 != NULL) {
__ Cmp(rbx, Handle<Map>(untransitioned_map_2));
__ j(equal, Handle<Code>(generic_stub), RelocInfo::CODE_TARGET);
}
__ bind(&miss);
Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic, RelocInfo::CODE_TARGET);
__ bind(&just_store);
__ jmp(Handle<Code>(notransition_stub), RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(NORMAL, NULL, MEGAMORPHIC);
}
MaybeObject* KeyedStoreStubCompiler::CompileStoreMegamorphic(
MapList* receiver_maps,
CodeList* handler_ics) {
......@@ -3694,7 +3744,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
// This stub is meant to be tail-jumped to, the receiver must already
// have been verified by the caller to not be a smi.
......@@ -3717,13 +3767,13 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
__ j(above_equal, &miss_force_generic);
}
// Do the store and update the write barrier.
if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
__ JumpIfNotSmi(rax, &miss_force_generic);
__ JumpIfNotSmi(rax, &transition_elements_kind);
__ SmiToInteger32(rcx, rcx);
__ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize),
rax);
} else {
// Do the store and update the write barrier.
ASSERT(elements_kind == FAST_ELEMENTS);
__ SmiToInteger32(rcx, rcx);
__ lea(rcx,
......@@ -3742,6 +3792,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(
Handle<Code> ic_force_generic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
}
......@@ -3754,7 +3808,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
Label miss_force_generic;
Label miss_force_generic, transition_elements_kind;
// This stub is meant to be tail-jumped to, the receiver must already
// have been verified by the caller to not be a smi.
......@@ -3776,7 +3830,8 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
// Handle smi values specially
__ SmiToInteger32(rcx, rcx);
__ StoreNumberToDoubleElements(rax, rdi, rcx, xmm0, &miss_force_generic);
__ StoreNumberToDoubleElements(rax, rdi, rcx, xmm0,
&transition_elements_kind);
__ ret(0);
// Handle store cache miss, replacing the ic with the generic stub.
......@@ -3784,6 +3839,10 @@ void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
Handle<Code> ic_force_generic =
masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
__ jmp(ic_force_generic, RelocInfo::CODE_TARGET);
__ bind(&transition_elements_kind);
Handle<Code> ic_miss = masm->isolate()->builtins()->KeyedStoreIC_Miss();
__ jmp(ic_miss, RelocInfo::CODE_TARGET);
}
......
......@@ -183,7 +183,6 @@ polymorphic(doubles, support_smi_only_arrays
// Crankshaft support for smi-only elements in dynamic array literals.
function get(foo) { return foo; } // Used to generate dynamic values.
//function crankshaft_test(expected_kind) {
function crankshaft_test() {
var a = [get(1), get(2), get(3)];
assertKind(element_kind.fast_smi_only_elements, a);
......@@ -204,3 +203,55 @@ for (var i = 0; i < 3; i++) {
}
%OptimizeFunctionOnNextCall(crankshaft_test);
crankshaft_test();
// Elements_kind transitions for arrays.
// A map can have three different elements_kind transitions: SMI->DOUBLE,
// DOUBLE->OBJECT, and SMI->OBJECT. No matter in which order these three are
// created, they must always end up with the same FAST map.
// Preparation: create one pair of identical objects for each case.
var a = [1, 2, 3];
var b = [1, 2, 3];
assertTrue(%HaveSameMap(a, b));
assertKind(element_kind.fast_smi_only_elements, a);
var c = [1, 2, 3];
c["case2"] = true;
var d = [1, 2, 3];
d["case2"] = true;
assertTrue(%HaveSameMap(c, d));
assertFalse(%HaveSameMap(a, c));
assertKind(element_kind.fast_smi_only_elements, c);
var e = [1, 2, 3];
e["case3"] = true;
var f = [1, 2, 3];
f["case3"] = true;
assertTrue(%HaveSameMap(e, f));
assertFalse(%HaveSameMap(a, e));
assertFalse(%HaveSameMap(c, e));
assertKind(element_kind.fast_smi_only_elements, e);
// Case 1: SMI->DOUBLE, DOUBLE->OBJECT, SMI->OBJECT.
a[0] = 1.5;
assertKind(element_kind.fast_double_elements, a);
a[0] = "foo";
assertKind(element_kind.fast_elements, a);
b[0] = "bar";
assertTrue(%HaveSameMap(a, b));
// Case 2: SMI->DOUBLE, SMI->OBJECT, DOUBLE->OBJECT.
c[0] = 1.5;
assertKind(element_kind.fast_double_elements, c);
assertFalse(%HaveSameMap(c, d));
d[0] = "foo";
assertKind(element_kind.fast_elements, d);
assertFalse(%HaveSameMap(c, d));
c[0] = "bar";
assertTrue(%HaveSameMap(c, d));
// Case 3: SMI->OBJECT, SMI->DOUBLE, DOUBLE->OBJECT.
e[0] = "foo";
assertKind(element_kind.fast_elements, e);
assertFalse(%HaveSameMap(e, f));
f[0] = 1.5;
assertKind(element_kind.fast_double_elements, f);
assertFalse(%HaveSameMap(e, f));
f[0] = "bar";
assertKind(element_kind.fast_elements, f);
assertTrue(%HaveSameMap(e, f));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment