Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / v8 / src / ic / x64 / ic-x64.cc
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.
4
5 #include "src/v8.h"
6
7 #if V8_TARGET_ARCH_X64
8
9 #include "src/codegen.h"
10 #include "src/ic/ic.h"
11 #include "src/ic/ic-compiler.h"
12 #include "src/ic/stub-cache.h"
13
14 namespace v8 {
15 namespace internal {
16
17 // ----------------------------------------------------------------------------
18 // Static IC stub generators.
19 //
20
21 #define __ ACCESS_MASM(masm)
22
23
24 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type,
25                                             Label* global_object) {
26   // Register usage:
27   //   type: holds the receiver instance type on entry.
28   __ cmpb(type, Immediate(JS_GLOBAL_OBJECT_TYPE));
29   __ j(equal, global_object);
30   __ cmpb(type, Immediate(JS_BUILTINS_OBJECT_TYPE));
31   __ j(equal, global_object);
32   __ cmpb(type, Immediate(JS_GLOBAL_PROXY_TYPE));
33   __ j(equal, global_object);
34 }
35
36
37 // Helper function used to load a property from a dictionary backing storage.
38 // This function may return false negatives, so miss_label
39 // must always call a backup property load that is complete.
40 // This function is safe to call if name is not an internalized string,
41 // and will jump to the miss_label in that case.
42 // The generated code assumes that the receiver has slow properties,
43 // is not a global object and does not have interceptors.
44 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
45                                    Register elements, Register name,
46                                    Register r0, Register r1, Register result) {
47   // Register use:
48   //
49   // elements - holds the property dictionary on entry and is unchanged.
50   //
51   // name - holds the name of the property on entry and is unchanged.
52   //
53   // r0   - used to hold the capacity of the property dictionary.
54   //
55   // r1   - used to hold the index into the property dictionary.
56   //
57   // result - holds the result on exit if the load succeeded.
58
59   Label done;
60
61   // Probe the dictionary.
62   NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
63                                                    elements, name, r0, r1);
64
65   // If probing finds an entry in the dictionary, r1 contains the
66   // index into the dictionary. Check that the value is a normal
67   // property.
68   __ bind(&done);
69   const int kElementsStartOffset =
70       NameDictionary::kHeaderSize +
71       NameDictionary::kElementsStartIndex * kPointerSize;
72   const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
73   __ Test(Operand(elements, r1, times_pointer_size,
74                   kDetailsOffset - kHeapObjectTag),
75           Smi::FromInt(PropertyDetails::TypeField::kMask));
76   __ j(not_zero, miss_label);
77
78   // Get the value at the masked, scaled index.
79   const int kValueOffset = kElementsStartOffset + kPointerSize;
80   __ movp(result, Operand(elements, r1, times_pointer_size,
81                           kValueOffset - kHeapObjectTag));
82 }
83
84
85 // Helper function used to store a property to a dictionary backing
86 // storage. This function may fail to store a property even though it
87 // is in the dictionary, so code at miss_label must always call a
88 // backup property store that is complete. This function is safe to
89 // call if name is not an internalized string, and will jump to the miss_label
90 // in that case. The generated code assumes that the receiver has slow
91 // properties, is not a global object and does not have interceptors.
92 static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss_label,
93                                     Register elements, Register name,
94                                     Register value, Register scratch0,
95                                     Register scratch1) {
96   // Register use:
97   //
98   // elements - holds the property dictionary on entry and is clobbered.
99   //
100   // name - holds the name of the property on entry and is unchanged.
101   //
102   // value - holds the value to store and is unchanged.
103   //
104   // scratch0 - used during the positive dictionary lookup and is clobbered.
105   //
106   // scratch1 - used for index into the property dictionary and is clobbered.
107   Label done;
108
109   // Probe the dictionary.
110   NameDictionaryLookupStub::GeneratePositiveLookup(
111       masm, miss_label, &done, elements, name, scratch0, scratch1);
112
113   // If probing finds an entry in the dictionary, scratch0 contains the
114   // index into the dictionary. Check that the value is a normal
115   // property that is not read only.
116   __ bind(&done);
117   const int kElementsStartOffset =
118       NameDictionary::kHeaderSize +
119       NameDictionary::kElementsStartIndex * kPointerSize;
120   const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
121   const int kTypeAndReadOnlyMask =
122       (PropertyDetails::TypeField::kMask |
123        PropertyDetails::AttributesField::encode(READ_ONLY))
124       << kSmiTagSize;
125   __ Test(Operand(elements, scratch1, times_pointer_size,
126                   kDetailsOffset - kHeapObjectTag),
127           Smi::FromInt(kTypeAndReadOnlyMask));
128   __ j(not_zero, miss_label);
129
130   // Store the value at the masked, scaled index.
131   const int kValueOffset = kElementsStartOffset + kPointerSize;
132   __ leap(scratch1, Operand(elements, scratch1, times_pointer_size,
133                             kValueOffset - kHeapObjectTag));
134   __ movp(Operand(scratch1, 0), value);
135
136   // Update write barrier. Make sure not to clobber the value.
137   __ movp(scratch0, value);
138   __ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs);
139 }
140
141
142 // Checks the receiver for special cases (value type, slow case bits).
143 // Falls through for regular JS object.
144 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
145                                            Register receiver, Register map,
146                                            int interceptor_bit, Label* slow) {
147   // Register use:
148   //   receiver - holds the receiver and is unchanged.
149   // Scratch registers:
150   //   map - used to hold the map of the receiver.
151
152   // Check that the object isn't a smi.
153   __ JumpIfSmi(receiver, slow);
154
155   // Check that the object is some kind of JS object EXCEPT JS Value type.
156   // In the case that the object is a value-wrapper object,
157   // we enter the runtime system to make sure that indexing
158   // into string objects work as intended.
159   DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE);
160   __ CmpObjectType(receiver, JS_OBJECT_TYPE, map);
161   __ j(below, slow);
162
163   // Check bit field.
164   __ testb(
165       FieldOperand(map, Map::kBitFieldOffset),
166       Immediate((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
167   __ j(not_zero, slow);
168 }
169
170
171 // Loads an indexed element from a fast case array.
172 // If not_fast_array is NULL, doesn't perform the elements map check.
173 static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver,
174                                   Register key, Register elements,
175                                   Register scratch, Register result,
176                                   Label* not_fast_array, Label* out_of_range) {
177   // Register use:
178   //
179   // receiver - holds the receiver on entry.
180   //            Unchanged unless 'result' is the same register.
181   //
182   // key      - holds the smi key on entry.
183   //            Unchanged unless 'result' is the same register.
184   //
185   // elements - holds the elements of the receiver on exit.
186   //
187   // result   - holds the result on exit if the load succeeded.
188   //            Allowed to be the the same as 'receiver' or 'key'.
189   //            Unchanged on bailout so 'receiver' and 'key' can be safely
190   //            used by further computation.
191   //
192   // Scratch registers:
193   //
194   //   scratch - used to hold elements of the receiver and the loaded value.
195
196   __ movp(elements, FieldOperand(receiver, JSObject::kElementsOffset));
197   if (not_fast_array != NULL) {
198     // Check that the object is in fast mode and writable.
199     __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset),
200                    Heap::kFixedArrayMapRootIndex);
201     __ j(not_equal, not_fast_array);
202   } else {
203     __ AssertFastElements(elements);
204   }
205   // Check that the key (index) is within bounds.
206   __ SmiCompare(key, FieldOperand(elements, FixedArray::kLengthOffset));
207   // Unsigned comparison rejects negative indices.
208   __ j(above_equal, out_of_range);
209   // Fast case: Do the load.
210   SmiIndex index = masm->SmiToIndex(scratch, key, kPointerSizeLog2);
211   __ movp(scratch, FieldOperand(elements, index.reg, index.scale,
212                                 FixedArray::kHeaderSize));
213   __ CompareRoot(scratch, Heap::kTheHoleValueRootIndex);
214   // In case the loaded value is the_hole we have to consult GetProperty
215   // to ensure the prototype chain is searched.
216   __ j(equal, out_of_range);
217   if (!result.is(scratch)) {
218     __ movp(result, scratch);
219   }
220 }
221
222
223 // Checks whether a key is an array index string or a unique name.
224 // Falls through if the key is a unique name.
225 static void GenerateKeyNameCheck(MacroAssembler* masm, Register key,
226                                  Register map, Register hash,
227                                  Label* index_string, Label* not_unique) {
228   // Register use:
229   //   key - holds the key and is unchanged. Assumed to be non-smi.
230   // Scratch registers:
231   //   map - used to hold the map of the key.
232   //   hash - used to hold the hash of the key.
233   Label unique;
234   __ CmpObjectType(key, LAST_UNIQUE_NAME_TYPE, map);
235   __ j(above, not_unique);
236   STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
237   __ j(equal, &unique);
238
239   // Is the string an array index, with cached numeric value?
240   __ movl(hash, FieldOperand(key, Name::kHashFieldOffset));
241   __ testl(hash, Immediate(Name::kContainsCachedArrayIndexMask));
242   __ j(zero, index_string);  // The value in hash is used at jump target.
243
244   // Is the string internalized? We already know it's a string so a single
245   // bit test is enough.
246   STATIC_ASSERT(kNotInternalizedTag != 0);
247   __ testb(FieldOperand(map, Map::kInstanceTypeOffset),
248            Immediate(kIsNotInternalizedMask));
249   __ j(not_zero, not_unique);
250
251   __ bind(&unique);
252 }
253
254
255 void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
256   // The return address is on the stack.
257   Label slow, check_name, index_smi, index_name, property_array_property;
258   Label probe_dictionary, check_number_dictionary;
259
260   Register receiver = LoadDescriptor::ReceiverRegister();
261   Register key = LoadDescriptor::NameRegister();
262   DCHECK(receiver.is(rdx));
263   DCHECK(key.is(rcx));
264
265   // Check that the key is a smi.
266   __ JumpIfNotSmi(key, &check_name);
267   __ bind(&index_smi);
268   // Now the key is known to be a smi. This place is also jumped to from below
269   // where a numeric string is converted to a smi.
270
271   GenerateKeyedLoadReceiverCheck(masm, receiver, rax,
272                                  Map::kHasIndexedInterceptor, &slow);
273
274   // Check the receiver's map to see if it has fast elements.
275   __ CheckFastElements(rax, &check_number_dictionary);
276
277   GenerateFastArrayLoad(masm, receiver, key, rax, rbx, rax, NULL, &slow);
278   Counters* counters = masm->isolate()->counters();
279   __ IncrementCounter(counters->keyed_load_generic_smi(), 1);
280   __ ret(0);
281
282   __ bind(&check_number_dictionary);
283   __ SmiToInteger32(rbx, key);
284   __ movp(rax, FieldOperand(receiver, JSObject::kElementsOffset));
285
286   // Check whether the elements is a number dictionary.
287   // rbx: key as untagged int32
288   // rax: elements
289   __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
290                  Heap::kHashTableMapRootIndex);
291   __ j(not_equal, &slow);
292   __ LoadFromNumberDictionary(&slow, rax, key, rbx, r9, rdi, rax);
293   __ ret(0);
294
295   __ bind(&slow);
296   // Slow case: Jump to runtime.
297   __ IncrementCounter(counters->keyed_load_generic_slow(), 1);
298   GenerateRuntimeGetProperty(masm);
299
300   __ bind(&check_name);
301   GenerateKeyNameCheck(masm, key, rax, rbx, &index_name, &slow);
302
303   GenerateKeyedLoadReceiverCheck(masm, receiver, rax, Map::kHasNamedInterceptor,
304                                  &slow);
305
306   // If the receiver is a fast-case object, check the keyed lookup
307   // cache. Otherwise probe the dictionary leaving result in key.
308   __ movp(rbx, FieldOperand(receiver, JSObject::kPropertiesOffset));
309   __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
310                  Heap::kHashTableMapRootIndex);
311   __ j(equal, &probe_dictionary);
312
313   // Load the map of the receiver, compute the keyed lookup cache hash
314   // based on 32 bits of the map pointer and the string hash.
315   __ movp(rbx, FieldOperand(receiver, HeapObject::kMapOffset));
316   __ movl(rax, rbx);
317   __ shrl(rax, Immediate(KeyedLookupCache::kMapHashShift));
318   __ movl(rdi, FieldOperand(key, String::kHashFieldOffset));
319   __ shrl(rdi, Immediate(String::kHashShift));
320   __ xorp(rax, rdi);
321   int mask = (KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask);
322   __ andp(rax, Immediate(mask));
323
324   // Load the key (consisting of map and internalized string) from the cache and
325   // check for match.
326   Label load_in_object_property;
327   static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
328   Label hit_on_nth_entry[kEntriesPerBucket];
329   ExternalReference cache_keys =
330       ExternalReference::keyed_lookup_cache_keys(masm->isolate());
331
332   for (int i = 0; i < kEntriesPerBucket - 1; i++) {
333     Label try_next_entry;
334     __ movp(rdi, rax);
335     __ shlp(rdi, Immediate(kPointerSizeLog2 + 1));
336     __ LoadAddress(kScratchRegister, cache_keys);
337     int off = kPointerSize * i * 2;
338     __ cmpp(rbx, Operand(kScratchRegister, rdi, times_1, off));
339     __ j(not_equal, &try_next_entry);
340     __ cmpp(key, Operand(kScratchRegister, rdi, times_1, off + kPointerSize));
341     __ j(equal, &hit_on_nth_entry[i]);
342     __ bind(&try_next_entry);
343   }
344
345   int off = kPointerSize * (kEntriesPerBucket - 1) * 2;
346   __ cmpp(rbx, Operand(kScratchRegister, rdi, times_1, off));
347   __ j(not_equal, &slow);
348   __ cmpp(key, Operand(kScratchRegister, rdi, times_1, off + kPointerSize));
349   __ j(not_equal, &slow);
350
351   // Get field offset, which is a 32-bit integer.
352   ExternalReference cache_field_offsets =
353       ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate());
354
355   // Hit on nth entry.
356   for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
357     __ bind(&hit_on_nth_entry[i]);
358     if (i != 0) {
359       __ addl(rax, Immediate(i));
360     }
361     __ LoadAddress(kScratchRegister, cache_field_offsets);
362     __ movl(rdi, Operand(kScratchRegister, rax, times_4, 0));
363     __ movzxbp(rax, FieldOperand(rbx, Map::kInObjectPropertiesOffset));
364     __ subp(rdi, rax);
365     __ j(above_equal, &property_array_property);
366     if (i != 0) {
367       __ jmp(&load_in_object_property);
368     }
369   }
370
371   // Load in-object property.
372   __ bind(&load_in_object_property);
373   __ movzxbp(rax, FieldOperand(rbx, Map::kInstanceSizeOffset));
374   __ addp(rax, rdi);
375   __ movp(rax, FieldOperand(receiver, rax, times_pointer_size, 0));
376   __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
377   __ ret(0);
378
379   // Load property array property.
380   __ bind(&property_array_property);
381   __ movp(rax, FieldOperand(receiver, JSObject::kPropertiesOffset));
382   __ movp(rax,
383           FieldOperand(rax, rdi, times_pointer_size, FixedArray::kHeaderSize));
384   __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1);
385   __ ret(0);
386
387   // Do a quick inline probe of the receiver's dictionary, if it
388   // exists.
389   __ bind(&probe_dictionary);
390   // rbx: elements
391
392   __ movp(rax, FieldOperand(receiver, JSObject::kMapOffset));
393   __ movb(rax, FieldOperand(rax, Map::kInstanceTypeOffset));
394   GenerateGlobalInstanceTypeCheck(masm, rax, &slow);
395
396   GenerateDictionaryLoad(masm, &slow, rbx, key, rax, rdi, rax);
397   __ IncrementCounter(counters->keyed_load_generic_symbol(), 1);
398   __ ret(0);
399
400   __ bind(&index_name);
401   __ IndexFromHash(rbx, key);
402   __ jmp(&index_smi);
403 }
404
405
406 void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
407   // Return address is on the stack.
408   Label miss;
409
410   Register receiver = LoadDescriptor::ReceiverRegister();
411   Register index = LoadDescriptor::NameRegister();
412   Register scratch = rbx;
413   Register result = rax;
414   DCHECK(!scratch.is(receiver) && !scratch.is(index));
415
416   StringCharAtGenerator char_at_generator(receiver, index, scratch, result,
417                                           &miss,  // When not a string.
418                                           &miss,  // When not a number.
419                                           &miss,  // When index out of range.
420                                           STRING_INDEX_IS_ARRAY_INDEX);
421   char_at_generator.GenerateFast(masm);
422   __ ret(0);
423
424   StubRuntimeCallHelper call_helper;
425   char_at_generator.GenerateSlow(masm, call_helper);
426
427   __ bind(&miss);
428   GenerateMiss(masm);
429 }
430
431
432 static void KeyedStoreGenerateGenericHelper(
433     MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
434     KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length) {
435   Label transition_smi_elements;
436   Label finish_object_store, non_double_value, transition_double_elements;
437   Label fast_double_without_map_check;
438   Register receiver = StoreDescriptor::ReceiverRegister();
439   Register key = StoreDescriptor::NameRegister();
440   Register value = StoreDescriptor::ValueRegister();
441   DCHECK(receiver.is(rdx));
442   DCHECK(key.is(rcx));
443   DCHECK(value.is(rax));
444   // Fast case: Do the store, could be either Object or double.
445   __ bind(fast_object);
446   // rbx: receiver's elements array (a FixedArray)
447   // receiver is a JSArray.
448   // r9: map of receiver
449   if (check_map == kCheckMap) {
450     __ movp(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
451     __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex);
452     __ j(not_equal, fast_double);
453   }
454
455   // HOLECHECK: guards "A[i] = V"
456   // We have to go to the runtime if the current value is the hole because
457   // there may be a callback on the element
458   Label holecheck_passed1;
459   __ movp(kScratchRegister,
460           FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize));
461   __ CompareRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
462   __ j(not_equal, &holecheck_passed1);
463   __ JumpIfDictionaryInPrototypeChain(receiver, rdi, kScratchRegister, slow);
464
465   __ bind(&holecheck_passed1);
466
467   // Smi stores don't require further checks.
468   Label non_smi_value;
469   __ JumpIfNotSmi(value, &non_smi_value);
470   if (increment_length == kIncrementLength) {
471     // Add 1 to receiver->length.
472     __ leal(rdi, Operand(key, 1));
473     __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi);
474   }
475   // It's irrelevant whether array is smi-only or not when writing a smi.
476   __ movp(FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize),
477           value);
478   __ ret(0);
479
480   __ bind(&non_smi_value);
481   // Writing a non-smi, check whether array allows non-smi elements.
482   // r9: receiver's map
483   __ CheckFastObjectElements(r9, &transition_smi_elements);
484
485   __ bind(&finish_object_store);
486   if (increment_length == kIncrementLength) {
487     // Add 1 to receiver->length.
488     __ leal(rdi, Operand(key, 1));
489     __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi);
490   }
491   __ movp(FieldOperand(rbx, key, times_pointer_size, FixedArray::kHeaderSize),
492           value);
493   __ movp(rdx, value);  // Preserve the value which is returned.
494   __ RecordWriteArray(rbx, rdx, key, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
495                       OMIT_SMI_CHECK);
496   __ ret(0);
497
498   __ bind(fast_double);
499   if (check_map == kCheckMap) {
500     // Check for fast double array case. If this fails, call through to the
501     // runtime.
502     // rdi: elements array's map
503     __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
504     __ j(not_equal, slow);
505   }
506
507   // HOLECHECK: guards "A[i] double hole?"
508   // We have to see if the double version of the hole is present. If so
509   // go to the runtime.
510   uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
511   __ cmpl(FieldOperand(rbx, key, times_8, offset), Immediate(kHoleNanUpper32));
512   __ j(not_equal, &fast_double_without_map_check);
513   __ JumpIfDictionaryInPrototypeChain(receiver, rdi, kScratchRegister, slow);
514
515   __ bind(&fast_double_without_map_check);
516   __ StoreNumberToDoubleElements(value, rbx, key, xmm0,
517                                  &transition_double_elements);
518   if (increment_length == kIncrementLength) {
519     // Add 1 to receiver->length.
520     __ leal(rdi, Operand(key, 1));
521     __ Integer32ToSmiField(FieldOperand(receiver, JSArray::kLengthOffset), rdi);
522   }
523   __ ret(0);
524
525   __ bind(&transition_smi_elements);
526   __ movp(rbx, FieldOperand(receiver, HeapObject::kMapOffset));
527
528   // Transition the array appropriately depending on the value type.
529   __ movp(r9, FieldOperand(value, HeapObject::kMapOffset));
530   __ CompareRoot(r9, Heap::kHeapNumberMapRootIndex);
531   __ j(not_equal, &non_double_value);
532
533   // Value is a double. Transition FAST_SMI_ELEMENTS ->
534   // FAST_DOUBLE_ELEMENTS and complete the store.
535   __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
536                                          FAST_DOUBLE_ELEMENTS, rbx, rdi, slow);
537   AllocationSiteMode mode =
538       AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS);
539   ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value,
540                                                    rbx, mode, slow);
541   __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset));
542   __ jmp(&fast_double_without_map_check);
543
544   __ bind(&non_double_value);
545   // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
546   __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, rbx,
547                                          rdi, slow);
548   mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
549   ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
550       masm, receiver, key, value, rbx, mode, slow);
551   __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset));
552   __ jmp(&finish_object_store);
553
554   __ bind(&transition_double_elements);
555   // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
556   // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
557   // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
558   __ movp(rbx, FieldOperand(receiver, HeapObject::kMapOffset));
559   __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS,
560                                          rbx, rdi, slow);
561   mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
562   ElementsTransitionGenerator::GenerateDoubleToObject(masm, receiver, key,
563                                                       value, rbx, mode, slow);
564   __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset));
565   __ jmp(&finish_object_store);
566 }
567
568
569 void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
570                                    StrictMode strict_mode) {
571   // Return address is on the stack.
572   Label slow, slow_with_tagged_index, fast_object, fast_object_grow;
573   Label fast_double, fast_double_grow;
574   Label array, extra, check_if_double_array;
575   Register receiver = StoreDescriptor::ReceiverRegister();
576   Register key = StoreDescriptor::NameRegister();
577   DCHECK(receiver.is(rdx));
578   DCHECK(key.is(rcx));
579
580   // Check that the object isn't a smi.
581   __ JumpIfSmi(receiver, &slow_with_tagged_index);
582   // Get the map from the receiver.
583   __ movp(r9, FieldOperand(receiver, HeapObject::kMapOffset));
584   // Check that the receiver does not require access checks and is not observed.
585   // The generic stub does not perform map checks or handle observed objects.
586   __ testb(FieldOperand(r9, Map::kBitFieldOffset),
587            Immediate(1 << Map::kIsAccessCheckNeeded | 1 << Map::kIsObserved));
588   __ j(not_zero, &slow_with_tagged_index);
589   // Check that the key is a smi.
590   __ JumpIfNotSmi(key, &slow_with_tagged_index);
591   __ SmiToInteger32(key, key);
592
593   __ CmpInstanceType(r9, JS_ARRAY_TYPE);
594   __ j(equal, &array);
595   // Check that the object is some kind of JSObject.
596   __ CmpInstanceType(r9, FIRST_JS_OBJECT_TYPE);
597   __ j(below, &slow);
598
599   // Object case: Check key against length in the elements array.
600   __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset));
601   // Check array bounds.
602   __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), key);
603   // rbx: FixedArray
604   __ j(above, &fast_object);
605
606   // Slow case: call runtime.
607   __ bind(&slow);
608   __ Integer32ToSmi(key, key);
609   __ bind(&slow_with_tagged_index);
610   PropertyICCompiler::GenerateRuntimeSetProperty(masm, strict_mode);
611   // Never returns to here.
612
613   // Extra capacity case: Check if there is extra capacity to
614   // perform the store and update the length. Used for adding one
615   // element to the array by writing to array[array.length].
616   __ bind(&extra);
617   // receiver is a JSArray.
618   // rbx: receiver's elements array (a FixedArray)
619   // flags: smicompare (receiver.length(), rbx)
620   __ j(not_equal, &slow);  // do not leave holes in the array
621   __ SmiCompareInteger32(FieldOperand(rbx, FixedArray::kLengthOffset), key);
622   __ j(below_equal, &slow);
623   // Increment index to get new length.
624   __ movp(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
625   __ CompareRoot(rdi, Heap::kFixedArrayMapRootIndex);
626   __ j(not_equal, &check_if_double_array);
627   __ jmp(&fast_object_grow);
628
629   __ bind(&check_if_double_array);
630   // rdi: elements array's map
631   __ CompareRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex);
632   __ j(not_equal, &slow);
633   __ jmp(&fast_double_grow);
634
635   // Array case: Get the length and the elements array from the JS
636   // array. Check that the array is in fast mode (and writable); if it
637   // is the length is always a smi.
638   __ bind(&array);
639   // receiver is a JSArray.
640   __ movp(rbx, FieldOperand(receiver, JSObject::kElementsOffset));
641
642   // Check the key against the length in the array, compute the
643   // address to store into and fall through to fast case.
644   __ SmiCompareInteger32(FieldOperand(receiver, JSArray::kLengthOffset), key);
645   __ j(below_equal, &extra);
646
647   KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double, &slow,
648                                   kCheckMap, kDontIncrementLength);
649   KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
650                                   &slow, kDontCheckMap, kIncrementLength);
651 }
652
653
654 static Operand GenerateMappedArgumentsLookup(
655     MacroAssembler* masm, Register object, Register key, Register scratch1,
656     Register scratch2, Register scratch3, Label* unmapped_case,
657     Label* slow_case) {
658   Heap* heap = masm->isolate()->heap();
659
660   // Check that the receiver is a JSObject. Because of the elements
661   // map check later, we do not need to check for interceptors or
662   // whether it requires access checks.
663   __ JumpIfSmi(object, slow_case);
664   // Check that the object is some kind of JSObject.
665   __ CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, scratch1);
666   __ j(below, slow_case);
667
668   // Check that the key is a positive smi.
669   Condition check = masm->CheckNonNegativeSmi(key);
670   __ j(NegateCondition(check), slow_case);
671
672   // Load the elements into scratch1 and check its map. If not, jump
673   // to the unmapped lookup with the parameter map in scratch1.
674   Handle<Map> arguments_map(heap->sloppy_arguments_elements_map());
675   __ movp(scratch1, FieldOperand(object, JSObject::kElementsOffset));
676   __ CheckMap(scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK);
677
678   // Check if element is in the range of mapped arguments.
679   __ movp(scratch2, FieldOperand(scratch1, FixedArray::kLengthOffset));
680   __ SmiSubConstant(scratch2, scratch2, Smi::FromInt(2));
681   __ cmpp(key, scratch2);
682   __ j(greater_equal, unmapped_case);
683
684   // Load element index and check whether it is the hole.
685   const int kHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize;
686   __ SmiToInteger64(scratch3, key);
687   __ movp(scratch2,
688           FieldOperand(scratch1, scratch3, times_pointer_size, kHeaderSize));
689   __ CompareRoot(scratch2, Heap::kTheHoleValueRootIndex);
690   __ j(equal, unmapped_case);
691
692   // Load value from context and return it. We can reuse scratch1 because
693   // we do not jump to the unmapped lookup (which requires the parameter
694   // map in scratch1).
695   __ movp(scratch1, FieldOperand(scratch1, FixedArray::kHeaderSize));
696   __ SmiToInteger64(scratch3, scratch2);
697   return FieldOperand(scratch1, scratch3, times_pointer_size,
698                       Context::kHeaderSize);
699 }
700
701
702 static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
703                                                Register key,
704                                                Register parameter_map,
705                                                Register scratch,
706                                                Label* slow_case) {
707   // Element is in arguments backing store, which is referenced by the
708   // second element of the parameter_map. The parameter_map register
709   // must be loaded with the parameter map of the arguments object and is
710   // overwritten.
711   const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
712   Register backing_store = parameter_map;
713   __ movp(backing_store, FieldOperand(parameter_map, kBackingStoreOffset));
714   Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map());
715   __ CheckMap(backing_store, fixed_array_map, slow_case, DONT_DO_SMI_CHECK);
716   __ movp(scratch, FieldOperand(backing_store, FixedArray::kLengthOffset));
717   __ cmpp(key, scratch);
718   __ j(greater_equal, slow_case);
719   __ SmiToInteger64(scratch, key);
720   return FieldOperand(backing_store, scratch, times_pointer_size,
721                       FixedArray::kHeaderSize);
722 }
723
724
725 void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
726   // The return address is on the stack.
727   Label slow, notin;
728   Register receiver = StoreDescriptor::ReceiverRegister();
729   Register name = StoreDescriptor::NameRegister();
730   Register value = StoreDescriptor::ValueRegister();
731   DCHECK(receiver.is(rdx));
732   DCHECK(name.is(rcx));
733   DCHECK(value.is(rax));
734
735   Operand mapped_location = GenerateMappedArgumentsLookup(
736       masm, receiver, name, rbx, rdi, r8, &notin, &slow);
737   __ movp(mapped_location, value);
738   __ leap(r9, mapped_location);
739   __ movp(r8, value);
740   __ RecordWrite(rbx, r9, r8, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
741                  INLINE_SMI_CHECK);
742   __ Ret();
743   __ bind(&notin);
744   // The unmapped lookup expects that the parameter map is in rbx.
745   Operand unmapped_location =
746       GenerateUnmappedArgumentsLookup(masm, name, rbx, rdi, &slow);
747   __ movp(unmapped_location, value);
748   __ leap(r9, unmapped_location);
749   __ movp(r8, value);
750   __ RecordWrite(rbx, r9, r8, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
751                  INLINE_SMI_CHECK);
752   __ Ret();
753   __ bind(&slow);
754   GenerateMiss(masm);
755 }
756
757
758 void LoadIC::GenerateNormal(MacroAssembler* masm) {
759   Register dictionary = rax;
760   DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
761   DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
762
763   Label slow;
764
765   __ movp(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(),
766                                    JSObject::kPropertiesOffset));
767   GenerateDictionaryLoad(masm, &slow, dictionary,
768                          LoadDescriptor::NameRegister(), rbx, rdi, rax);
769   __ ret(0);
770
771   // Dictionary load failed, go slow (but don't miss).
772   __ bind(&slow);
773   GenerateRuntimeGetProperty(masm);
774 }
775
776
777 // A register that isn't one of the parameters to the load ic.
778 static const Register LoadIC_TempRegister() { return rbx; }
779
780
781 static const Register KeyedLoadIC_TempRegister() { return rbx; }
782
783
784 void LoadIC::GenerateMiss(MacroAssembler* masm) {
785   // The return address is on the stack.
786
787   Counters* counters = masm->isolate()->counters();
788   __ IncrementCounter(counters->load_miss(), 1);
789
790   __ PopReturnAddressTo(LoadIC_TempRegister());
791   __ Push(LoadDescriptor::ReceiverRegister());  // receiver
792   __ Push(LoadDescriptor::NameRegister());      // name
793   __ PushReturnAddressFrom(LoadIC_TempRegister());
794
795   // Perform tail call to the entry.
796   ExternalReference ref =
797       ExternalReference(IC_Utility(kLoadIC_Miss), masm->isolate());
798   __ TailCallExternalReference(ref, 2, 1);
799 }
800
801
802 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
803   // The return address is on the stack.
804
805   __ PopReturnAddressTo(LoadIC_TempRegister());
806   __ Push(LoadDescriptor::ReceiverRegister());  // receiver
807   __ Push(LoadDescriptor::NameRegister());      // name
808   __ PushReturnAddressFrom(LoadIC_TempRegister());
809
810   // Perform tail call to the entry.
811   __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
812 }
813
814
815 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
816   // The return address is on the stack.
817   Counters* counters = masm->isolate()->counters();
818   __ IncrementCounter(counters->keyed_load_miss(), 1);
819
820   __ PopReturnAddressTo(KeyedLoadIC_TempRegister());
821   __ Push(LoadDescriptor::ReceiverRegister());  // receiver
822   __ Push(LoadDescriptor::NameRegister());      // name
823   __ PushReturnAddressFrom(KeyedLoadIC_TempRegister());
824
825   // Perform tail call to the entry.
826   ExternalReference ref =
827       ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate());
828   __ TailCallExternalReference(ref, 2, 1);
829 }
830
831
832 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
833   // The return address is on the stack.
834
835   __ PopReturnAddressTo(KeyedLoadIC_TempRegister());
836   __ Push(LoadDescriptor::ReceiverRegister());  // receiver
837   __ Push(LoadDescriptor::NameRegister());      // name
838   __ PushReturnAddressFrom(KeyedLoadIC_TempRegister());
839
840   // Perform tail call to the entry.
841   __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
842 }
843
844
845 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
846   // The return address is on the stack.
847
848   // Get the receiver from the stack and probe the stub cache.
849   Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
850       Code::ComputeHandlerFlags(Code::STORE_IC));
851   masm->isolate()->stub_cache()->GenerateProbe(
852       masm, flags, false, StoreDescriptor::ReceiverRegister(),
853       StoreDescriptor::NameRegister(), rbx, no_reg);
854
855   // Cache miss: Jump to runtime.
856   GenerateMiss(masm);
857 }
858
859
860 static void StoreIC_PushArgs(MacroAssembler* masm) {
861   Register receiver = StoreDescriptor::ReceiverRegister();
862   Register name = StoreDescriptor::NameRegister();
863   Register value = StoreDescriptor::ValueRegister();
864
865   DCHECK(!rbx.is(receiver) && !rbx.is(name) && !rbx.is(value));
866
867   __ PopReturnAddressTo(rbx);
868   __ Push(receiver);
869   __ Push(name);
870   __ Push(value);
871   __ PushReturnAddressFrom(rbx);
872 }
873
874
875 void StoreIC::GenerateMiss(MacroAssembler* masm) {
876   // Return address is on the stack.
877   StoreIC_PushArgs(masm);
878
879   // Perform tail call to the entry.
880   ExternalReference ref =
881       ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
882   __ TailCallExternalReference(ref, 3, 1);
883 }
884
885
886 void StoreIC::GenerateNormal(MacroAssembler* masm) {
887   Register receiver = StoreDescriptor::ReceiverRegister();
888   Register name = StoreDescriptor::NameRegister();
889   Register value = StoreDescriptor::ValueRegister();
890   Register dictionary = rbx;
891
892   Label miss;
893
894   __ movp(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
895   GenerateDictionaryStore(masm, &miss, dictionary, name, value, r8, r9);
896   Counters* counters = masm->isolate()->counters();
897   __ IncrementCounter(counters->store_normal_hit(), 1);
898   __ ret(0);
899
900   __ bind(&miss);
901   __ IncrementCounter(counters->store_normal_miss(), 1);
902   GenerateMiss(masm);
903 }
904
905
906 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
907   // Return address is on the stack.
908   StoreIC_PushArgs(masm);
909
910   // Do tail-call to runtime routine.
911   ExternalReference ref =
912       ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
913   __ TailCallExternalReference(ref, 3, 1);
914 }
915
916
917 #undef __
918
919
920 Condition CompareIC::ComputeCondition(Token::Value op) {
921   switch (op) {
922     case Token::EQ_STRICT:
923     case Token::EQ:
924       return equal;
925     case Token::LT:
926       return less;
927     case Token::GT:
928       return greater;
929     case Token::LTE:
930       return less_equal;
931     case Token::GTE:
932       return greater_equal;
933     default:
934       UNREACHABLE();
935       return no_condition;
936   }
937 }
938
939
940 bool CompareIC::HasInlinedSmiCode(Address address) {
941   // The address of the instruction following the call.
942   Address test_instruction_address =
943       address + Assembler::kCallTargetAddressOffset;
944
945   // If the instruction following the call is not a test al, nothing
946   // was inlined.
947   return *test_instruction_address == Assembler::kTestAlByte;
948 }
949
950
951 void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
952   // The address of the instruction following the call.
953   Address test_instruction_address =
954       address + Assembler::kCallTargetAddressOffset;
955
956   // If the instruction following the call is not a test al, nothing
957   // was inlined.
958   if (*test_instruction_address != Assembler::kTestAlByte) {
959     DCHECK(*test_instruction_address == Assembler::kNopByte);
960     return;
961   }
962
963   Address delta_address = test_instruction_address + 1;
964   // The delta to the start of the map check instruction and the
965   // condition code uses at the patched jump.
966   uint8_t delta = *reinterpret_cast<uint8_t*>(delta_address);
967   if (FLAG_trace_ic) {
968     PrintF("[  patching ic at %p, test=%p, delta=%d\n", address,
969            test_instruction_address, delta);
970   }
971
972   // Patch with a short conditional jump. Enabling means switching from a short
973   // jump-if-carry/not-carry to jump-if-zero/not-zero, whereas disabling is the
974   // reverse operation of that.
975   Address jmp_address = test_instruction_address - delta;
976   DCHECK((check == ENABLE_INLINED_SMI_CHECK)
977              ? (*jmp_address == Assembler::kJncShortOpcode ||
978                 *jmp_address == Assembler::kJcShortOpcode)
979              : (*jmp_address == Assembler::kJnzShortOpcode ||
980                 *jmp_address == Assembler::kJzShortOpcode));
981   Condition cc =
982       (check == ENABLE_INLINED_SMI_CHECK)
983           ? (*jmp_address == Assembler::kJncShortOpcode ? not_zero : zero)
984           : (*jmp_address == Assembler::kJnzShortOpcode ? not_carry : carry);
985   *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc);
986 }
987 }
988 }  // namespace v8::internal
989
990 #endif  // V8_TARGET_ARCH_X64