1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #if V8_TARGET_ARCH_IA32
9 #include "src/codegen.h"
10 #include "src/ic-inl.h"
11 #include "src/runtime.h"
12 #include "src/stub-cache.h"
17 // ----------------------------------------------------------------------------
18 // Static IC stub generators.
21 #define __ ACCESS_MASM(masm)
24 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
26 Label* global_object) {
28 // type: holds the receiver instance type on entry.
29 __ cmp(type, JS_GLOBAL_OBJECT_TYPE);
30 __ j(equal, global_object);
31 __ cmp(type, JS_BUILTINS_OBJECT_TYPE);
32 __ j(equal, global_object);
33 __ cmp(type, JS_GLOBAL_PROXY_TYPE);
34 __ j(equal, global_object);
38 // Helper function used to load a property from a dictionary backing
39 // storage. This function may fail to load a property even though it is
40 // in the dictionary, so code at miss_label must always call a backup
41 // property load that is complete. This function is safe to call if
42 // name is not internalized, and will jump to the miss_label in that
43 // case. The generated code assumes that the receiver has slow
44 // properties, is not a global object and does not have interceptors.
45 static void GenerateDictionaryLoad(MacroAssembler* masm,
54 // elements - holds the property dictionary on entry and is unchanged.
56 // name - holds the name of the property on entry and is unchanged.
60 // r0 - used for the index into the property dictionary
62 // r1 - used to hold the capacity of the property dictionary.
64 // result - holds the result on exit.
68 // Probe the dictionary.
69 NameDictionaryLookupStub::GeneratePositiveLookup(masm,
77 // If probing finds an entry in the dictionary, r0 contains the
78 // index into the dictionary. Check that the value is a normal
81 const int kElementsStartOffset =
82 NameDictionary::kHeaderSize +
83 NameDictionary::kElementsStartIndex * kPointerSize;
84 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
85 __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
86 Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize));
87 __ j(not_zero, miss_label);
89 // Get the value at the masked, scaled index.
90 const int kValueOffset = kElementsStartOffset + kPointerSize;
91 __ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
95 // Helper function used to store a property to a dictionary backing
96 // storage. This function may fail to store a property eventhough it
97 // is in the dictionary, so code at miss_label must always call a
98 // backup property store that is complete. This function is safe to
99 // call if name is not internalized, and will jump to the miss_label in
100 // that case. The generated code assumes that the receiver has slow
101 // properties, is not a global object and does not have interceptors.
102 static void GenerateDictionaryStore(MacroAssembler* masm,
111 // elements - holds the property dictionary on entry and is clobbered.
113 // name - holds the name of the property on entry and is unchanged.
115 // value - holds the value to store and is unchanged.
117 // r0 - used for index into the property dictionary and is clobbered.
119 // r1 - used to hold the capacity of the property dictionary and is clobbered.
123 // Probe the dictionary.
124 NameDictionaryLookupStub::GeneratePositiveLookup(masm,
132 // If probing finds an entry in the dictionary, r0 contains the
133 // index into the dictionary. Check that the value is a normal
134 // property that is not read only.
136 const int kElementsStartOffset =
137 NameDictionary::kHeaderSize +
138 NameDictionary::kElementsStartIndex * kPointerSize;
139 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
140 const int kTypeAndReadOnlyMask =
141 (PropertyDetails::TypeField::kMask |
142 PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
143 __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
144 Immediate(kTypeAndReadOnlyMask));
145 __ j(not_zero, miss_label);
147 // Store the value at the masked, scaled index.
148 const int kValueOffset = kElementsStartOffset + kPointerSize;
149 __ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
150 __ mov(Operand(r0, 0), value);
152 // Update write barrier. Make sure not to clobber the value.
154 __ RecordWrite(elements, r0, r1, kDontSaveFPRegs);
158 // Checks the receiver for special cases (value type, slow case bits).
159 // Falls through for regular JS object.
160 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
166 // receiver - holds the receiver and is unchanged.
167 // Scratch registers:
168 // map - used to hold the map of the receiver.
170 // Check that the object isn't a smi.
171 __ JumpIfSmi(receiver, slow);
173 // Get the map of the receiver.
174 __ mov(map, FieldOperand(receiver, HeapObject::kMapOffset));
177 __ test_b(FieldOperand(map, Map::kBitFieldOffset),
178 (1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit));
179 __ j(not_zero, slow);
180 // Check that the object is some kind of JS object EXCEPT JS Value type.
181 // In the case that the object is a value-wrapper object,
182 // we enter the runtime system to make sure that indexing
183 // into string objects works as intended.
184 DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE);
186 __ CmpInstanceType(map, JS_OBJECT_TYPE);
191 // Loads an indexed element from a fast case array.
192 // If not_fast_array is NULL, doesn't perform the elements map check.
193 static void GenerateFastArrayLoad(MacroAssembler* masm,
198 Label* not_fast_array,
199 Label* out_of_range) {
201 // receiver - holds the receiver and is unchanged.
202 // key - holds the key and is unchanged (must be a smi).
203 // Scratch registers:
204 // scratch - used to hold elements of the receiver and the loaded value.
205 // result - holds the result on exit if the load succeeds and
208 __ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset));
209 if (not_fast_array != NULL) {
210 // Check that the object is in fast mode and writable.
212 masm->isolate()->factory()->fixed_array_map(),
216 __ AssertFastElements(scratch);
218 // Check that the key (index) is within bounds.
219 __ cmp(key, FieldOperand(scratch, FixedArray::kLengthOffset));
220 __ j(above_equal, out_of_range);
221 // Fast case: Do the load.
222 STATIC_ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0));
223 __ mov(scratch, FieldOperand(scratch, key, times_2, FixedArray::kHeaderSize));
224 __ cmp(scratch, Immediate(masm->isolate()->factory()->the_hole_value()));
225 // In case the loaded value is the_hole we have to consult GetProperty
226 // to ensure the prototype chain is searched.
227 __ j(equal, out_of_range);
228 if (!result.is(scratch)) {
229 __ mov(result, scratch);
234 // Checks whether a key is an array index string or a unique name.
235 // Falls through if the key is a unique name.
236 static void GenerateKeyNameCheck(MacroAssembler* masm,
243 // key - holds the key and is unchanged. Assumed to be non-smi.
244 // Scratch registers:
245 // map - used to hold the map of the key.
246 // hash - used to hold the hash of the key.
248 __ CmpObjectType(key, LAST_UNIQUE_NAME_TYPE, map);
249 __ j(above, not_unique);
250 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
251 __ j(equal, &unique);
253 // Is the string an array index, with cached numeric value?
254 __ mov(hash, FieldOperand(key, Name::kHashFieldOffset));
255 __ test(hash, Immediate(Name::kContainsCachedArrayIndexMask));
256 __ j(zero, index_string);
258 // Is the string internalized? We already know it's a string so a single
259 // bit test is enough.
260 STATIC_ASSERT(kNotInternalizedTag != 0);
261 __ test_b(FieldOperand(map, Map::kInstanceTypeOffset),
262 kIsNotInternalizedMask);
263 __ j(not_zero, not_unique);
269 static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm,
274 Label* unmapped_case,
276 Heap* heap = masm->isolate()->heap();
277 Factory* factory = masm->isolate()->factory();
279 // Check that the receiver is a JSObject. Because of the elements
280 // map check later, we do not need to check for interceptors or
281 // whether it requires access checks.
282 __ JumpIfSmi(object, slow_case);
283 // Check that the object is some kind of JSObject.
284 __ CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, scratch1);
285 __ j(below, slow_case);
287 // Check that the key is a positive smi.
288 __ test(key, Immediate(0x80000001));
289 __ j(not_zero, slow_case);
291 // Load the elements into scratch1 and check its map.
292 Handle<Map> arguments_map(heap->sloppy_arguments_elements_map());
293 __ mov(scratch1, FieldOperand(object, JSObject::kElementsOffset));
294 __ CheckMap(scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK);
296 // Check if element is in the range of mapped arguments. If not, jump
297 // to the unmapped lookup with the parameter map in scratch1.
298 __ mov(scratch2, FieldOperand(scratch1, FixedArray::kLengthOffset));
299 __ sub(scratch2, Immediate(Smi::FromInt(2)));
300 __ cmp(key, scratch2);
301 __ j(above_equal, unmapped_case);
303 // Load element index and check whether it is the hole.
304 const int kHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize;
305 __ mov(scratch2, FieldOperand(scratch1,
307 times_half_pointer_size,
309 __ cmp(scratch2, factory->the_hole_value());
310 __ j(equal, unmapped_case);
312 // Load value from context and return it. We can reuse scratch1 because
313 // we do not jump to the unmapped lookup (which requires the parameter
315 const int kContextOffset = FixedArray::kHeaderSize;
316 __ mov(scratch1, FieldOperand(scratch1, kContextOffset));
317 return FieldOperand(scratch1,
319 times_half_pointer_size,
320 Context::kHeaderSize);
324 static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
326 Register parameter_map,
329 // Element is in arguments backing store, which is referenced by the
330 // second element of the parameter_map.
331 const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
332 Register backing_store = parameter_map;
333 __ mov(backing_store, FieldOperand(parameter_map, kBackingStoreOffset));
334 Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map());
335 __ CheckMap(backing_store, fixed_array_map, slow_case, DONT_DO_SMI_CHECK);
336 __ mov(scratch, FieldOperand(backing_store, FixedArray::kLengthOffset));
337 __ cmp(key, scratch);
338 __ j(greater_equal, slow_case);
339 return FieldOperand(backing_store,
341 times_half_pointer_size,
342 FixedArray::kHeaderSize);
346 void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
347 // The return address is on the stack.
348 Label slow, check_name, index_smi, index_name, property_array_property;
349 Label probe_dictionary, check_number_dictionary;
351 Register receiver = ReceiverRegister();
352 Register key = NameRegister();
353 DCHECK(receiver.is(edx));
356 // Check that the key is a smi.
357 __ JumpIfNotSmi(key, &check_name);
359 // Now the key is known to be a smi. This place is also jumped to from
360 // where a numeric string is converted to a smi.
362 GenerateKeyedLoadReceiverCheck(
363 masm, receiver, eax, Map::kHasIndexedInterceptor, &slow);
365 // Check the receiver's map to see if it has fast elements.
366 __ CheckFastElements(eax, &check_number_dictionary);
368 GenerateFastArrayLoad(masm, receiver, key, eax, eax, NULL, &slow);
369 Isolate* isolate = masm->isolate();
370 Counters* counters = isolate->counters();
371 __ IncrementCounter(counters->keyed_load_generic_smi(), 1);
374 __ bind(&check_number_dictionary);
377 __ mov(eax, FieldOperand(receiver, JSObject::kElementsOffset));
379 // Check whether the elements is a number dictionary.
380 // ebx: untagged index
383 isolate->factory()->hash_table_map(),
386 Label slow_pop_receiver;
387 // Push receiver on the stack to free up a register for the dictionary
390 __ LoadFromNumberDictionary(&slow_pop_receiver, eax, key, ebx, edx, edi, eax);
391 // Pop receiver before returning.
395 __ bind(&slow_pop_receiver);
396 // Pop the receiver from the stack and jump to runtime.
400 // Slow case: jump to runtime.
401 __ IncrementCounter(counters->keyed_load_generic_slow(), 1);
402 GenerateRuntimeGetProperty(masm);
404 __ bind(&check_name);
405 GenerateKeyNameCheck(masm, key, eax, ebx, &index_name, &slow);
407 GenerateKeyedLoadReceiverCheck(
408 masm, receiver, eax, Map::kHasNamedInterceptor, &slow);
410 // If the receiver is a fast-case object, check the keyed lookup
411 // cache. Otherwise probe the dictionary.
412 __ mov(ebx, FieldOperand(receiver, JSObject::kPropertiesOffset));
413 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
414 Immediate(isolate->factory()->hash_table_map()));
415 __ j(equal, &probe_dictionary);
417 // The receiver's map is still in eax, compute the keyed lookup cache hash
418 // based on 32 bits of the map pointer and the string hash.
419 if (FLAG_debug_code) {
420 __ cmp(eax, FieldOperand(receiver, HeapObject::kMapOffset));
421 __ Check(equal, kMapIsNoLongerInEax);
423 __ mov(ebx, eax); // Keep the map around for later.
424 __ shr(eax, KeyedLookupCache::kMapHashShift);
425 __ mov(edi, FieldOperand(key, String::kHashFieldOffset));
426 __ shr(edi, String::kHashShift);
428 __ and_(eax, KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask);
430 // Load the key (consisting of map and internalized string) from the cache and
432 Label load_in_object_property;
433 static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
434 Label hit_on_nth_entry[kEntriesPerBucket];
435 ExternalReference cache_keys =
436 ExternalReference::keyed_lookup_cache_keys(masm->isolate());
438 for (int i = 0; i < kEntriesPerBucket - 1; i++) {
439 Label try_next_entry;
441 __ shl(edi, kPointerSizeLog2 + 1);
443 __ add(edi, Immediate(kPointerSize * i * 2));
445 __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
446 __ j(not_equal, &try_next_entry);
447 __ add(edi, Immediate(kPointerSize));
448 __ cmp(key, Operand::StaticArray(edi, times_1, cache_keys));
449 __ j(equal, &hit_on_nth_entry[i]);
450 __ bind(&try_next_entry);
453 __ lea(edi, Operand(eax, 1));
454 __ shl(edi, kPointerSizeLog2 + 1);
455 __ add(edi, Immediate(kPointerSize * (kEntriesPerBucket - 1) * 2));
456 __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
457 __ j(not_equal, &slow);
458 __ add(edi, Immediate(kPointerSize));
459 __ cmp(key, Operand::StaticArray(edi, times_1, cache_keys));
460 __ j(not_equal, &slow);
463 // ebx : receiver's map
464 // eax : lookup cache index
465 ExternalReference cache_field_offsets =
466 ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate());
469 for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
470 __ bind(&hit_on_nth_entry[i]);
472 __ add(eax, Immediate(i));
475 Operand::StaticArray(eax, times_pointer_size, cache_field_offsets));
476 __ movzx_b(eax, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
478 __ j(above_equal, &property_array_property);
480 __ jmp(&load_in_object_property);
484 // Load in-object property.
485 __ bind(&load_in_object_property);
486 __ movzx_b(eax, FieldOperand(ebx, Map::kInstanceSizeOffset));
488 __ mov(eax, FieldOperand(receiver, eax, times_pointer_size, 0));
489 __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
492 // Load property array property.
493 __ bind(&property_array_property);
494 __ mov(eax, FieldOperand(receiver, JSObject::kPropertiesOffset));
495 __ mov(eax, FieldOperand(eax, edi, times_pointer_size,
496 FixedArray::kHeaderSize));
497 __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
500 // Do a quick inline probe of the receiver's dictionary, if it
502 __ bind(&probe_dictionary);
504 __ mov(eax, FieldOperand(receiver, JSObject::kMapOffset));
505 __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
506 GenerateGlobalInstanceTypeCheck(masm, eax, &slow);
508 GenerateDictionaryLoad(masm, &slow, ebx, key, eax, edi, eax);
509 __ IncrementCounter(counters->keyed_load_generic_symbol(), 1);
512 __ bind(&index_name);
513 __ IndexFromHash(ebx, key);
514 // Now jump to the place where smi keys are handled.
519 void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
520 // Return address is on the stack.
523 Register receiver = ReceiverRegister();
524 Register index = NameRegister();
525 Register scratch = ebx;
526 DCHECK(!scratch.is(receiver) && !scratch.is(index));
527 Register result = eax;
528 DCHECK(!result.is(scratch));
530 StringCharAtGenerator char_at_generator(receiver,
534 &miss, // When not a string.
535 &miss, // When not a number.
536 &miss, // When index out of range.
537 STRING_INDEX_IS_ARRAY_INDEX);
538 char_at_generator.GenerateFast(masm);
541 StubRuntimeCallHelper call_helper;
542 char_at_generator.GenerateSlow(masm, call_helper);
549 void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
550 // Return address is on the stack.
553 Register receiver = ReceiverRegister();
554 Register key = NameRegister();
555 Register scratch = eax;
556 DCHECK(!scratch.is(receiver) && !scratch.is(key));
558 // Check that the receiver isn't a smi.
559 __ JumpIfSmi(receiver, &slow);
561 // Check that the key is an array index, that is Uint32.
562 __ test(key, Immediate(kSmiTagMask | kSmiSignMask));
563 __ j(not_zero, &slow);
565 // Get the map of the receiver.
566 __ mov(scratch, FieldOperand(receiver, HeapObject::kMapOffset));
568 // Check that it has indexed interceptor and access checks
569 // are not enabled for this object.
570 __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
571 __ and_(scratch, Immediate(kSlowCaseBitFieldMask));
572 __ cmp(scratch, Immediate(1 << Map::kHasIndexedInterceptor));
573 __ j(not_zero, &slow);
575 // Everything is fine, call runtime.
577 __ push(receiver); // receiver
579 __ push(scratch); // return address
581 // Perform tail call to the entry.
582 ExternalReference ref = ExternalReference(
583 IC_Utility(kLoadElementWithInterceptor), masm->isolate());
584 __ TailCallExternalReference(ref, 2, 1);
591 void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
592 // The return address is on the stack.
593 Register receiver = ReceiverRegister();
594 Register key = NameRegister();
595 DCHECK(receiver.is(edx));
599 Factory* factory = masm->isolate()->factory();
600 Operand mapped_location =
601 GenerateMappedArgumentsLookup(
602 masm, receiver, key, ebx, eax, ¬in, &slow);
603 __ mov(eax, mapped_location);
606 // The unmapped lookup expects that the parameter map is in ebx.
607 Operand unmapped_location =
608 GenerateUnmappedArgumentsLookup(masm, key, ebx, eax, &slow);
609 __ cmp(unmapped_location, factory->the_hole_value());
611 __ mov(eax, unmapped_location);
618 void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
619 // Return address is on the stack.
621 Register receiver = ReceiverRegister();
622 Register name = NameRegister();
623 Register value = ValueRegister();
624 DCHECK(receiver.is(edx));
625 DCHECK(name.is(ecx));
626 DCHECK(value.is(eax));
628 Operand mapped_location =
629 GenerateMappedArgumentsLookup(masm, receiver, name, ebx, edi, ¬in,
631 __ mov(mapped_location, value);
632 __ lea(ecx, mapped_location);
634 __ RecordWrite(ebx, ecx, edx, kDontSaveFPRegs);
637 // The unmapped lookup expects that the parameter map is in ebx.
638 Operand unmapped_location =
639 GenerateUnmappedArgumentsLookup(masm, name, ebx, edi, &slow);
640 __ mov(unmapped_location, value);
641 __ lea(edi, unmapped_location);
643 __ RecordWrite(ebx, edi, edx, kDontSaveFPRegs);
650 static void KeyedStoreGenerateGenericHelper(
651 MacroAssembler* masm,
655 KeyedStoreCheckMap check_map,
656 KeyedStoreIncrementLength increment_length) {
657 Label transition_smi_elements;
658 Label finish_object_store, non_double_value, transition_double_elements;
659 Label fast_double_without_map_check;
660 Register receiver = KeyedStoreIC::ReceiverRegister();
661 Register key = KeyedStoreIC::NameRegister();
662 Register value = KeyedStoreIC::ValueRegister();
663 DCHECK(receiver.is(edx));
665 DCHECK(value.is(eax));
667 // ebx: FixedArray receiver->elements
669 // Fast case: Do the store, could either Object or double.
670 __ bind(fast_object);
671 if (check_map == kCheckMap) {
672 __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset));
673 __ cmp(edi, masm->isolate()->factory()->fixed_array_map());
674 __ j(not_equal, fast_double);
677 // HOLECHECK: guards "A[i] = V"
678 // We have to go to the runtime if the current value is the hole because
679 // there may be a callback on the element
680 Label holecheck_passed1;
681 __ cmp(FixedArrayElementOperand(ebx, key),
682 masm->isolate()->factory()->the_hole_value());
683 __ j(not_equal, &holecheck_passed1);
684 __ JumpIfDictionaryInPrototypeChain(receiver, ebx, edi, slow);
685 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
687 __ bind(&holecheck_passed1);
689 // Smi stores don't require further checks.
691 __ JumpIfNotSmi(value, &non_smi_value);
692 if (increment_length == kIncrementLength) {
693 // Add 1 to receiver->length.
694 __ add(FieldOperand(receiver, JSArray::kLengthOffset),
695 Immediate(Smi::FromInt(1)));
697 // It's irrelevant whether array is smi-only or not when writing a smi.
698 __ mov(FixedArrayElementOperand(ebx, key), value);
701 __ bind(&non_smi_value);
702 // Escape to elements kind transition case.
703 __ mov(edi, FieldOperand(receiver, HeapObject::kMapOffset));
704 __ CheckFastObjectElements(edi, &transition_smi_elements);
706 // Fast elements array, store the value to the elements backing store.
707 __ bind(&finish_object_store);
708 if (increment_length == kIncrementLength) {
709 // Add 1 to receiver->length.
710 __ add(FieldOperand(receiver, JSArray::kLengthOffset),
711 Immediate(Smi::FromInt(1)));
713 __ mov(FixedArrayElementOperand(ebx, key), value);
714 // Update write barrier for the elements array address.
715 __ mov(edx, value); // Preserve the value which is returned.
717 ebx, edx, key, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
720 __ bind(fast_double);
721 if (check_map == kCheckMap) {
722 // Check for fast double array case. If this fails, call through to the
724 __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map());
725 __ j(not_equal, slow);
726 // If the value is a number, store it as a double in the FastDoubleElements
730 // HOLECHECK: guards "A[i] double hole?"
731 // We have to see if the double version of the hole is present. If so
732 // go to the runtime.
733 uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
734 __ cmp(FieldOperand(ebx, key, times_4, offset), Immediate(kHoleNanUpper32));
735 __ j(not_equal, &fast_double_without_map_check);
736 __ JumpIfDictionaryInPrototypeChain(receiver, ebx, edi, slow);
737 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
739 __ bind(&fast_double_without_map_check);
740 __ StoreNumberToDoubleElements(value, ebx, key, edi, xmm0,
741 &transition_double_elements);
742 if (increment_length == kIncrementLength) {
743 // Add 1 to receiver->length.
744 __ add(FieldOperand(receiver, JSArray::kLengthOffset),
745 Immediate(Smi::FromInt(1)));
749 __ bind(&transition_smi_elements);
750 __ mov(ebx, FieldOperand(receiver, HeapObject::kMapOffset));
752 // Transition the array appropriately depending on the value type.
754 masm->isolate()->factory()->heap_number_map(),
758 // Value is a double. Transition FAST_SMI_ELEMENTS -> FAST_DOUBLE_ELEMENTS
759 // and complete the store.
760 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
761 FAST_DOUBLE_ELEMENTS,
765 AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
766 FAST_DOUBLE_ELEMENTS);
767 ElementsTransitionGenerator::GenerateSmiToDouble(
768 masm, receiver, key, value, ebx, mode, slow);
769 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
770 __ jmp(&fast_double_without_map_check);
772 __ bind(&non_double_value);
773 // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
774 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
779 mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
780 ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
781 masm, receiver, key, value, ebx, mode, slow);
782 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
783 __ jmp(&finish_object_store);
785 __ bind(&transition_double_elements);
786 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
787 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
788 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
789 __ mov(ebx, FieldOperand(receiver, HeapObject::kMapOffset));
790 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS,
795 mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
796 ElementsTransitionGenerator::GenerateDoubleToObject(
797 masm, receiver, key, value, ebx, mode, slow);
798 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
799 __ jmp(&finish_object_store);
803 void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
804 StrictMode strict_mode) {
805 // Return address is on the stack.
806 Label slow, fast_object, fast_object_grow;
807 Label fast_double, fast_double_grow;
808 Label array, extra, check_if_double_array;
809 Register receiver = ReceiverRegister();
810 Register key = NameRegister();
811 DCHECK(receiver.is(edx));
814 // Check that the object isn't a smi.
815 __ JumpIfSmi(receiver, &slow);
816 // Get the map from the receiver.
817 __ mov(edi, FieldOperand(receiver, HeapObject::kMapOffset));
818 // Check that the receiver does not require access checks and is not observed.
819 // The generic stub does not perform map checks or handle observed objects.
820 __ test_b(FieldOperand(edi, Map::kBitFieldOffset),
821 1 << Map::kIsAccessCheckNeeded | 1 << Map::kIsObserved);
822 __ j(not_zero, &slow);
823 // Check that the key is a smi.
824 __ JumpIfNotSmi(key, &slow);
825 __ CmpInstanceType(edi, JS_ARRAY_TYPE);
827 // Check that the object is some kind of JSObject.
828 __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
831 // Object case: Check key against length in the elements array.
834 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
835 // Check array bounds. Both the key and the length of FixedArray are smis.
836 __ cmp(key, FieldOperand(ebx, FixedArray::kLengthOffset));
837 __ j(below, &fast_object);
839 // Slow case: call runtime.
841 GenerateRuntimeSetProperty(masm, strict_mode);
843 // Extra capacity case: Check if there is extra capacity to
844 // perform the store and update the length. Used for adding one
845 // element to the array by writing to array[array.length].
847 // receiver is a JSArray.
849 // ebx: receiver->elements, a FixedArray
851 // flags: compare (key, receiver.length())
852 // do not leave holes in the array:
853 __ j(not_equal, &slow);
854 __ cmp(key, FieldOperand(ebx, FixedArray::kLengthOffset));
855 __ j(above_equal, &slow);
856 __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset));
857 __ cmp(edi, masm->isolate()->factory()->fixed_array_map());
858 __ j(not_equal, &check_if_double_array);
859 __ jmp(&fast_object_grow);
861 __ bind(&check_if_double_array);
862 __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map());
863 __ j(not_equal, &slow);
864 __ jmp(&fast_double_grow);
866 // Array case: Get the length and the elements array from the JS
867 // array. Check that the array is in fast mode (and writable); if it
868 // is the length is always a smi.
870 // receiver is a JSArray.
873 __ mov(ebx, FieldOperand(receiver, JSObject::kElementsOffset));
875 // Check the key against the length in the array and fall through to the
876 // common store code.
877 __ cmp(key, FieldOperand(receiver, JSArray::kLengthOffset)); // Compare smis.
878 __ j(above_equal, &extra);
880 KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
881 &slow, kCheckMap, kDontIncrementLength);
882 KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
883 &slow, kDontCheckMap, kIncrementLength);
887 void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
888 // The return address is on the stack.
889 Register receiver = ReceiverRegister();
890 Register name = NameRegister();
891 DCHECK(receiver.is(edx));
892 DCHECK(name.is(ecx));
894 // Probe the stub cache.
895 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
896 Code::ComputeHandlerFlags(Code::LOAD_IC));
897 masm->isolate()->stub_cache()->GenerateProbe(
898 masm, flags, receiver, name, ebx, eax);
900 // Cache miss: Jump to runtime.
905 void LoadIC::GenerateNormal(MacroAssembler* masm) {
906 Register dictionary = eax;
907 DCHECK(!dictionary.is(ReceiverRegister()));
908 DCHECK(!dictionary.is(NameRegister()));
913 FieldOperand(ReceiverRegister(), JSObject::kPropertiesOffset));
914 GenerateDictionaryLoad(masm, &slow, dictionary, NameRegister(), edi, ebx,
918 // Dictionary load failed, go slow (but don't miss).
920 GenerateRuntimeGetProperty(masm);
924 static void LoadIC_PushArgs(MacroAssembler* masm) {
925 Register receiver = LoadIC::ReceiverRegister();
926 Register name = LoadIC::NameRegister();
927 DCHECK(!ebx.is(receiver) && !ebx.is(name));
936 void LoadIC::GenerateMiss(MacroAssembler* masm) {
937 // Return address is on the stack.
938 __ IncrementCounter(masm->isolate()->counters()->load_miss(), 1);
940 LoadIC_PushArgs(masm);
942 // Perform tail call to the entry.
943 ExternalReference ref =
944 ExternalReference(IC_Utility(kLoadIC_Miss), masm->isolate());
945 __ TailCallExternalReference(ref, 2, 1);
949 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
950 // Return address is on the stack.
951 LoadIC_PushArgs(masm);
953 // Perform tail call to the entry.
954 __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
958 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
959 // Return address is on the stack.
960 __ IncrementCounter(masm->isolate()->counters()->keyed_load_miss(), 1);
962 LoadIC_PushArgs(masm);
964 // Perform tail call to the entry.
965 ExternalReference ref =
966 ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
967 __ TailCallExternalReference(ref, 2, 1);
971 // IC register specifications
972 const Register LoadIC::ReceiverRegister() { return edx; }
973 const Register LoadIC::NameRegister() { return ecx; }
976 const Register LoadIC::SlotRegister() {
977 DCHECK(FLAG_vector_ics);
982 const Register LoadIC::VectorRegister() {
983 DCHECK(FLAG_vector_ics);
988 const Register StoreIC::ReceiverRegister() { return edx; }
989 const Register StoreIC::NameRegister() { return ecx; }
990 const Register StoreIC::ValueRegister() { return eax; }
993 const Register KeyedStoreIC::MapRegister() {
998 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
999 // Return address is on the stack.
1000 LoadIC_PushArgs(masm);
1002 // Perform tail call to the entry.
1003 __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
1007 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
1008 // Return address is on the stack.
1009 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
1010 Code::ComputeHandlerFlags(Code::STORE_IC));
1011 masm->isolate()->stub_cache()->GenerateProbe(
1012 masm, flags, ReceiverRegister(), NameRegister(),
1015 // Cache miss: Jump to runtime.
1020 static void StoreIC_PushArgs(MacroAssembler* masm) {
1021 Register receiver = StoreIC::ReceiverRegister();
1022 Register name = StoreIC::NameRegister();
1023 Register value = StoreIC::ValueRegister();
1025 DCHECK(!ebx.is(receiver) && !ebx.is(name) && !ebx.is(value));
1035 void StoreIC::GenerateMiss(MacroAssembler* masm) {
1036 // Return address is on the stack.
1037 StoreIC_PushArgs(masm);
1039 // Perform tail call to the entry.
1040 ExternalReference ref =
1041 ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
1042 __ TailCallExternalReference(ref, 3, 1);
1046 void StoreIC::GenerateNormal(MacroAssembler* masm) {
1048 Register receiver = ReceiverRegister();
1049 Register name = NameRegister();
1050 Register value = ValueRegister();
1051 Register dictionary = ebx;
1053 __ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
1055 // A lot of registers are needed for storing to slow case
1056 // objects. Push and restore receiver but rely on
1057 // GenerateDictionaryStore preserving the value and name.
1059 GenerateDictionaryStore(masm, &restore_miss, dictionary, name, value,
1062 Counters* counters = masm->isolate()->counters();
1063 __ IncrementCounter(counters->store_normal_hit(), 1);
1066 __ bind(&restore_miss);
1068 __ IncrementCounter(counters->store_normal_miss(), 1);
1073 void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
1074 StrictMode strict_mode) {
1075 // Return address is on the stack.
1076 DCHECK(!ebx.is(ReceiverRegister()) && !ebx.is(NameRegister()) &&
1077 !ebx.is(ValueRegister()));
1079 __ push(ReceiverRegister());
1080 __ push(NameRegister());
1081 __ push(ValueRegister());
1082 __ push(Immediate(Smi::FromInt(strict_mode)));
1083 __ push(ebx); // return address
1085 // Do tail-call to runtime routine.
1086 __ TailCallRuntime(Runtime::kSetProperty, 4, 1);
1090 void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
1091 StrictMode strict_mode) {
1092 // Return address is on the stack.
1093 DCHECK(!ebx.is(ReceiverRegister()) && !ebx.is(NameRegister()) &&
1094 !ebx.is(ValueRegister()));
1096 __ push(ReceiverRegister());
1097 __ push(NameRegister());
1098 __ push(ValueRegister());
1099 __ push(Immediate(Smi::FromInt(strict_mode)));
1100 __ push(ebx); // return address
1102 // Do tail-call to runtime routine.
1103 __ TailCallRuntime(Runtime::kSetProperty, 4, 1);
1107 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
1108 // Return address is on the stack.
1109 StoreIC_PushArgs(masm);
1111 // Do tail-call to runtime routine.
1112 ExternalReference ref =
1113 ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
1114 __ TailCallExternalReference(ref, 3, 1);
1118 void StoreIC::GenerateSlow(MacroAssembler* masm) {
1119 // Return address is on the stack.
1120 StoreIC_PushArgs(masm);
1122 // Do tail-call to runtime routine.
1123 ExternalReference ref(IC_Utility(kStoreIC_Slow), masm->isolate());
1124 __ TailCallExternalReference(ref, 3, 1);
1128 void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
1129 // Return address is on the stack.
1130 StoreIC_PushArgs(masm);
1132 // Do tail-call to runtime routine.
1133 ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
1134 __ TailCallExternalReference(ref, 3, 1);
1141 Condition CompareIC::ComputeCondition(Token::Value op) {
1143 case Token::EQ_STRICT:
1153 return greater_equal;
1156 return no_condition;
1161 bool CompareIC::HasInlinedSmiCode(Address address) {
1162 // The address of the instruction following the call.
1163 Address test_instruction_address =
1164 address + Assembler::kCallTargetAddressOffset;
1166 // If the instruction following the call is not a test al, nothing
1168 return *test_instruction_address == Assembler::kTestAlByte;
1172 void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
1173 // The address of the instruction following the call.
1174 Address test_instruction_address =
1175 address + Assembler::kCallTargetAddressOffset;
1177 // If the instruction following the call is not a test al, nothing
1179 if (*test_instruction_address != Assembler::kTestAlByte) {
1180 DCHECK(*test_instruction_address == Assembler::kNopByte);
1184 Address delta_address = test_instruction_address + 1;
1185 // The delta to the start of the map check instruction and the
1186 // condition code uses at the patched jump.
1187 uint8_t delta = *reinterpret_cast<uint8_t*>(delta_address);
1188 if (FLAG_trace_ic) {
1189 PrintF("[ patching ic at %p, test=%p, delta=%d\n",
1190 address, test_instruction_address, delta);
1193 // Patch with a short conditional jump. Enabling means switching from a short
1194 // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the
1195 // reverse operation of that.
1196 Address jmp_address = test_instruction_address - delta;
1197 DCHECK((check == ENABLE_INLINED_SMI_CHECK)
1198 ? (*jmp_address == Assembler::kJncShortOpcode ||
1199 *jmp_address == Assembler::kJcShortOpcode)
1200 : (*jmp_address == Assembler::kJnzShortOpcode ||
1201 *jmp_address == Assembler::kJzShortOpcode));
1202 Condition cc = (check == ENABLE_INLINED_SMI_CHECK)
1203 ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero)
1204 : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry);
1205 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc);
1209 } } // namespace v8::internal
1211 #endif // V8_TARGET_ARCH_IA32