}
+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) {
// -- r3 : scratch
// -- r4 : scratch (elements)
// -----------------------------------
- Label miss_force_generic;
+ Label miss_force_generic, transition_elements_kind;
Register value_reg = r0;
Register key_reg = r1;
__ 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));
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);
}
// -- r4 : scratch
// -- r5 : scratch
// -----------------------------------
- Label miss_force_generic;
+ Label miss_force_generic, transition_elements_kind;
Register value_reg = r0;
Register key_reg = r1;
scratch2,
scratch3,
scratch4,
- &miss_force_generic);
+ &transition_elements_kind);
__ Ret();
// Handle store cache miss, replacing the ic with the generic stub.
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);
}
}
+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) {
// -- 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.
}
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,
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);
}
// -- 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.
ecx,
edx,
xmm0,
- &miss_force_generic,
+ &transition_elements_kind,
true);
__ ret(0);
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);
}
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() ?
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;
// 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;
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;
MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
- bool is_store,
+ StubKind stub_kind,
StrictModeFlag strict_mode,
Code* generic_stub) {
Code* result = NULL;
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;
}
+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) {
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() ?
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() {}
virtual Code::Kind kind() const = 0;
MaybeObject* ComputeStub(JSObject* receiver,
- bool is_store,
+ StubKind stub_kind,
StrictModeFlag strict_mode,
Code* default_stub);
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;
+ }
};
}
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());
}
-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.
!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
(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;
}
} 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));
// 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;
}
}
#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
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)
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());
#include "allocation.h"
#include "arguments.h"
+#include "ic-inl.h"
#include "macro-assembler.h"
#include "objects.h"
#include "zone-inl.h"
MUST_USE_RESULT MaybeObject* ComputeKeyedLoadOrStoreElement(
JSObject* receiver,
- bool is_store,
+ KeyedIC::StubKind stub_kind,
StrictModeFlag strict_mode);
// ---
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);
}
+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) {
// -- 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.
__ 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,
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);
}
// -- 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.
// 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.
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);
}
// 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);
}
%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));