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
GenerateGlobalInstanceTypeCheck(MacroAssembler * masm,Register type,Label * global_object)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.
GenerateDictionaryLoad(MacroAssembler * masm,Label * miss_label,Register elements,Register name,Register r0,Register r1,Register result)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.
GenerateDictionaryStore(MacroAssembler * masm,Label * miss_label,Register elements,Register name,Register value,Register scratch0,Register scratch1)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.
GenerateKeyedLoadReceiverCheck(MacroAssembler * masm,Register receiver,Register map,int interceptor_bit,Label * slow)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.
GenerateFastArrayLoad(MacroAssembler * masm,Register receiver,Register key,Register elements,Register scratch,Register result,Label * not_fast_array,Label * out_of_range)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.
GenerateKeyNameCheck(MacroAssembler * masm,Register key,Register map,Register hash,Label * index_string,Label * not_unique)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
GenerateGeneric(MacroAssembler * masm)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
GenerateString(MacroAssembler * masm)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
KeyedStoreGenerateGenericHelper(MacroAssembler * masm,Label * fast_object,Label * fast_double,Label * slow,KeyedStoreCheckMap check_map,KeyedStoreIncrementLength increment_length)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
GenerateGeneric(MacroAssembler * masm,StrictMode strict_mode)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
GenerateMappedArgumentsLookup(MacroAssembler * masm,Register object,Register key,Register scratch1,Register scratch2,Register scratch3,Label * unmapped_case,Label * slow_case)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
GenerateUnmappedArgumentsLookup(MacroAssembler * masm,Register key,Register parameter_map,Register scratch,Label * slow_case)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
GenerateSloppyArguments(MacroAssembler * masm)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, ¬in, &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(¬in);
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
GenerateNormal(MacroAssembler * masm)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.
LoadIC_TempRegister()778 static const Register LoadIC_TempRegister() { return rbx; }
779
780
KeyedLoadIC_TempRegister()781 static const Register KeyedLoadIC_TempRegister() { return rbx; }
782
783
GenerateMiss(MacroAssembler * masm)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
GenerateRuntimeGetProperty(MacroAssembler * masm)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
GenerateMiss(MacroAssembler * masm)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
GenerateRuntimeGetProperty(MacroAssembler * masm)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
GenerateMegamorphic(MacroAssembler * masm)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
StoreIC_PushArgs(MacroAssembler * masm)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
GenerateMiss(MacroAssembler * masm)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
GenerateNormal(MacroAssembler * masm)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
GenerateMiss(MacroAssembler * masm)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
ComputeCondition(Token::Value op)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
HasInlinedSmiCode(Address address)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
PatchInlinedSmiCode(Address address,InlinedSmiCheck check)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
991