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
This commit is contained in:
parent
f900fc9d80
commit
184fdcf28b
@ -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(¬ransition_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(¬ransition_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);
|
||||
}
|
||||
|
||||
|
||||
|
113
src/ic.cc
113
src/ic.cc
@ -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() ?
|
||||
|
25
src/ic.h
25
src/ic.h
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
203
src/objects.cc
203
src/objects.cc
@ -1097,7 +1097,7 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
|
||||
}
|
||||
switch (map()->instance_type()) {
|
||||
case MAP_TYPE:
|
||||
accumulator->Add("<Map>");
|
||||
accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind());
|
||||
break;
|
||||
case FIXED_ARRAY_TYPE:
|
||||
accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
|
||||
@ -2163,13 +2163,135 @@ static MaybeObject* AddElementsTransitionMapToDescriptor(
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
|
||||
// Returns the contents of |map|'s descriptor array for the given string
|
||||
// (which might be NULL). |safe_to_add_transition| is set to false and NULL
|
||||
// is returned if adding transitions is not allowed.
|
||||
static Object* GetDescriptorContents(Map* map,
|
||||
String* sentinel_name,
|
||||
bool* safe_to_add_transition) {
|
||||
// Get the cached index for the descriptors lookup, or find and cache it.
|
||||
DescriptorArray* descriptors = map->instance_descriptors();
|
||||
DescriptorLookupCache* cache = map->GetIsolate()->descriptor_lookup_cache();
|
||||
int index = cache->Lookup(descriptors, sentinel_name);
|
||||
if (index == DescriptorLookupCache::kAbsent) {
|
||||
index = descriptors->Search(sentinel_name);
|
||||
cache->Update(descriptors, sentinel_name, index);
|
||||
}
|
||||
// If the transition already exists, return its descriptor.
|
||||
if (index != DescriptorArray::kNotFound) {
|
||||
PropertyDetails details(descriptors->GetDetails(index));
|
||||
if (details.type() == ELEMENTS_TRANSITION) {
|
||||
return descriptors->GetValue(index);
|
||||
} else {
|
||||
*safe_to_add_transition = false;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Returns the map that |original_map| transitions to if its elements_kind
|
||||
// is changed to |elements_kind|, or NULL if no such map is cached yet.
|
||||
// |safe_to_add_transitions| is set to false if adding transitions is not
|
||||
// allowed.
|
||||
static Map* LookupElementsTransitionMap(Map* original_map,
|
||||
ElementsKind elements_kind,
|
||||
String* sentinel_name,
|
||||
bool* safe_to_add_transition) {
|
||||
// Special case: indirect SMI->FAST transition (cf. comment in
|
||||
// AddElementsTransition()).
|
||||
if (original_map->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
|
||||
elements_kind == FAST_ELEMENTS) {
|
||||
Map* double_map = LookupElementsTransitionMap(
|
||||
original_map,
|
||||
FAST_DOUBLE_ELEMENTS,
|
||||
sentinel_name,
|
||||
safe_to_add_transition);
|
||||
if (double_map == NULL) return double_map;
|
||||
return LookupElementsTransitionMap(double_map,
|
||||
FAST_ELEMENTS,
|
||||
sentinel_name,
|
||||
safe_to_add_transition);
|
||||
}
|
||||
Object* descriptor_contents = GetDescriptorContents(
|
||||
original_map, sentinel_name, safe_to_add_transition);
|
||||
if (descriptor_contents != NULL) {
|
||||
Map* maybe_transition_map =
|
||||
GetElementsTransitionMapFromDescriptor(descriptor_contents,
|
||||
elements_kind);
|
||||
ASSERT(maybe_transition_map == NULL || maybe_transition_map->IsMap());
|
||||
return maybe_transition_map;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Adds an entry to |original_map|'s descriptor array for a transition to
|
||||
// |transitioned_map| when its elements_kind is changed to |elements_kind|,
|
||||
// using |sentinel_name| as key for the entry.
|
||||
static MaybeObject* AddElementsTransition(Map* original_map,
|
||||
ElementsKind elements_kind,
|
||||
Map* transitioned_map,
|
||||
String* sentinel_name) {
|
||||
// The map transition graph should be a tree, therefore the transition
|
||||
// from SMI to FAST elements is not done directly, but by going through
|
||||
// DOUBLE elements first.
|
||||
if (original_map->elements_kind() == FAST_SMI_ONLY_ELEMENTS &&
|
||||
elements_kind == FAST_ELEMENTS) {
|
||||
bool safe_to_add = true;
|
||||
Map* double_map = LookupElementsTransitionMap(
|
||||
original_map, FAST_DOUBLE_ELEMENTS, sentinel_name, &safe_to_add);
|
||||
// This method is only called when safe_to_add_transition has been found
|
||||
// to be true earlier.
|
||||
ASSERT(safe_to_add);
|
||||
|
||||
if (double_map == NULL) {
|
||||
MaybeObject* maybe_map = original_map->CopyDropTransitions();
|
||||
if (!maybe_map->To(&double_map)) return maybe_map;
|
||||
double_map->set_elements_kind(FAST_DOUBLE_ELEMENTS);
|
||||
MaybeObject* maybe_double_transition = AddElementsTransition(
|
||||
original_map, FAST_DOUBLE_ELEMENTS, double_map, sentinel_name);
|
||||
if (maybe_double_transition->IsFailure()) return maybe_double_transition;
|
||||
}
|
||||
return AddElementsTransition(
|
||||
double_map, FAST_ELEMENTS, transitioned_map, sentinel_name);
|
||||
}
|
||||
|
||||
DescriptorArray* descriptors = original_map->instance_descriptors();
|
||||
bool safe_to_add_transition = true;
|
||||
Object* descriptor_contents = GetDescriptorContents(
|
||||
original_map, sentinel_name, &safe_to_add_transition);
|
||||
// This method is only called when safe_to_add_transition has been found
|
||||
// to be true earlier.
|
||||
ASSERT(safe_to_add_transition);
|
||||
MaybeObject* maybe_new_contents =
|
||||
AddElementsTransitionMapToDescriptor(descriptor_contents,
|
||||
transitioned_map);
|
||||
Object* new_contents;
|
||||
if (!maybe_new_contents->ToObject(&new_contents)) {
|
||||
return maybe_new_contents;
|
||||
}
|
||||
|
||||
ElementsTransitionDescriptor desc(sentinel_name, new_contents);
|
||||
Object* new_descriptors;
|
||||
MaybeObject* maybe_new_descriptors =
|
||||
descriptors->CopyInsert(&desc, KEEP_TRANSITIONS);
|
||||
if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
||||
return maybe_new_descriptors;
|
||||
}
|
||||
descriptors = DescriptorArray::cast(new_descriptors);
|
||||
original_map->set_instance_descriptors(descriptors);
|
||||
return original_map;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind to_kind) {
|
||||
Heap* current_heap = GetHeap();
|
||||
Map* current_map = map();
|
||||
DescriptorArray* descriptors = current_map->instance_descriptors();
|
||||
ElementsKind from_kind = current_map->elements_kind();
|
||||
String* elements_transition_sentinel_name = current_heap->empty_symbol();
|
||||
|
||||
if (current_map->elements_kind() == elements_kind) return current_map;
|
||||
if (from_kind == to_kind) return current_map;
|
||||
|
||||
// Only objects with FastProperties can have DescriptorArrays and can track
|
||||
// element-related maps. Also don't add descriptors to maps that are shared.
|
||||
@ -2177,58 +2299,33 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
|
||||
!current_map->IsUndefined() &&
|
||||
!current_map->is_shared();
|
||||
|
||||
// Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps cause by objects
|
||||
// Prevent long chains of DICTIONARY -> FAST_ELEMENTS maps caused by objects
|
||||
// with elements that switch back and forth between dictionary and fast
|
||||
// element mode.
|
||||
if ((current_map->elements_kind() == DICTIONARY_ELEMENTS &&
|
||||
elements_kind == FAST_ELEMENTS)) {
|
||||
if (from_kind == DICTIONARY_ELEMENTS && to_kind == FAST_ELEMENTS) {
|
||||
safe_to_add_transition = false;
|
||||
}
|
||||
|
||||
Object* descriptor_contents = NULL;
|
||||
if (safe_to_add_transition) {
|
||||
// It's only safe to manipulate the descriptor array if it would be
|
||||
// safe to add a transition.
|
||||
|
||||
// Check if the elements transition already exists.
|
||||
DescriptorLookupCache* cache =
|
||||
current_heap->isolate()->descriptor_lookup_cache();
|
||||
int index = cache->Lookup(descriptors, elements_transition_sentinel_name);
|
||||
if (index == DescriptorLookupCache::kAbsent) {
|
||||
index = descriptors->Search(elements_transition_sentinel_name);
|
||||
cache->Update(descriptors,
|
||||
elements_transition_sentinel_name,
|
||||
index);
|
||||
}
|
||||
|
||||
// If the transition already exists, check the type. If there is a match,
|
||||
// return it.
|
||||
if (index != DescriptorArray::kNotFound) {
|
||||
PropertyDetails details(PropertyDetails(descriptors->GetDetails(index)));
|
||||
if (details.type() == ELEMENTS_TRANSITION) {
|
||||
descriptor_contents = descriptors->GetValue(index);
|
||||
Map* maybe_transition_map =
|
||||
GetElementsTransitionMapFromDescriptor(descriptor_contents,
|
||||
elements_kind);
|
||||
if (maybe_transition_map != NULL) {
|
||||
ASSERT(maybe_transition_map->IsMap());
|
||||
return maybe_transition_map;
|
||||
}
|
||||
} else {
|
||||
safe_to_add_transition = false;
|
||||
}
|
||||
Map* maybe_transition_map = LookupElementsTransitionMap(
|
||||
current_map, to_kind, elements_transition_sentinel_name,
|
||||
&safe_to_add_transition);
|
||||
if (maybe_transition_map != NULL) {
|
||||
return maybe_transition_map;
|
||||
}
|
||||
}
|
||||
|
||||
Map* new_map = NULL;
|
||||
|
||||
// No transition to an existing map for the given ElementsKind. Make a new
|
||||
// one.
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_map = current_map->CopyDropTransitions();
|
||||
if (!maybe_map->ToObject(&obj)) return maybe_map;
|
||||
if (!maybe_map->To(&new_map)) return maybe_map;
|
||||
}
|
||||
Map* new_map = Map::cast(obj);
|
||||
|
||||
new_map->set_elements_kind(elements_kind);
|
||||
new_map->set_elements_kind(to_kind);
|
||||
|
||||
// Only remember the map transition if the object's map is NOT equal to the
|
||||
// global object_function's map and there is not an already existing
|
||||
@ -2237,26 +2334,10 @@ MaybeObject* JSObject::GetElementsTransitionMap(ElementsKind elements_kind) {
|
||||
(GetIsolate()->context()->global_context()->object_function()->map() !=
|
||||
map());
|
||||
if (allow_map_transition) {
|
||||
MaybeObject* maybe_new_contents =
|
||||
AddElementsTransitionMapToDescriptor(descriptor_contents, new_map);
|
||||
Object* new_contents;
|
||||
if (!maybe_new_contents->ToObject(&new_contents)) {
|
||||
return maybe_new_contents;
|
||||
}
|
||||
|
||||
ElementsTransitionDescriptor desc(elements_transition_sentinel_name,
|
||||
new_contents);
|
||||
Object* new_descriptors;
|
||||
MaybeObject* maybe_new_descriptors = descriptors->CopyInsert(
|
||||
&desc,
|
||||
KEEP_TRANSITIONS);
|
||||
if (!maybe_new_descriptors->ToObject(&new_descriptors)) {
|
||||
return maybe_new_descriptors;
|
||||
}
|
||||
descriptors = DescriptorArray::cast(new_descriptors);
|
||||
current_map->set_instance_descriptors(descriptors);
|
||||
MaybeObject* maybe_transition = AddElementsTransition(
|
||||
current_map, to_kind, new_map, elements_transition_sentinel_name);
|
||||
if (maybe_transition->IsFailure()) return maybe_transition;
|
||||
}
|
||||
|
||||
return new_map;
|
||||
}
|
||||
|
||||
@ -6709,7 +6790,7 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
|
||||
} else {
|
||||
ASSERT(object->IsFixedArray());
|
||||
ASSERT(details.type() == ELEMENTS_TRANSITION);
|
||||
FixedArray* array = reinterpret_cast<FixedArray*>(contents->get(i));
|
||||
FixedArray* array = reinterpret_cast<FixedArray*>(object);
|
||||
bool reachable_map_found = false;
|
||||
for (int j = 0; j < array->length(); ++j) {
|
||||
Map* target = reinterpret_cast<Map*>(array->get(j));
|
||||
@ -6723,7 +6804,7 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) {
|
||||
// Getter prototype() is read-only, set_prototype() has side
|
||||
// effects.
|
||||
*RawField(target, Map::kPrototypeOffset) = real_prototype;
|
||||
} else {
|
||||
} else if (target->IsMap()) {
|
||||
reachable_map_found = true;
|
||||
}
|
||||
}
|
||||
|
@ -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(¬ransition_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));
|
||||
|
Loading…
Reference in New Issue
Block a user