1 // Copyright 2013 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_ARM64
9 #include "src/arm64/assembler-arm64.h"
10 #include "src/code-stubs.h"
11 #include "src/codegen.h"
12 #include "src/disasm.h"
13 #include "src/ic-inl.h"
14 #include "src/runtime.h"
15 #include "src/stub-cache.h"
21 #define __ ACCESS_MASM(masm)
24 // "type" holds an instance type on entry and is not clobbered.
25 // Generated code branch on "global_object" if type is any kind of global
27 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
29 Label* global_object) {
30 __ Cmp(type, JS_GLOBAL_OBJECT_TYPE);
31 __ Ccmp(type, JS_BUILTINS_OBJECT_TYPE, ZFlag, ne);
32 __ Ccmp(type, JS_GLOBAL_PROXY_TYPE, ZFlag, ne);
33 __ B(eq, global_object);
37 // Helper function used from LoadIC GenerateNormal.
39 // elements: Property dictionary. It is not clobbered if a jump to the miss
41 // name: Property name. It is not clobbered if a jump to the miss label is
43 // result: Register for the result. It is only updated if a jump to the miss
45 // The scratch registers need to be different from elements, name and result.
46 // The generated code assumes that the receiver has slow properties,
47 // is not a global object and does not have interceptors.
48 static void GenerateDictionaryLoad(MacroAssembler* masm,
55 DCHECK(!AreAliased(elements, name, scratch1, scratch2));
56 DCHECK(!AreAliased(result, scratch1, scratch2));
60 // Probe the dictionary.
61 NameDictionaryLookupStub::GeneratePositiveLookup(masm,
69 // If probing finds an entry check that the value is a normal property.
72 static const int kElementsStartOffset = NameDictionary::kHeaderSize +
73 NameDictionary::kElementsStartIndex * kPointerSize;
74 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
75 __ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
76 __ Tst(scratch1, Smi::FromInt(PropertyDetails::TypeField::kMask));
79 // Get the value at the masked, scaled index and return.
81 FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
85 // Helper function used from StoreIC::GenerateNormal.
87 // elements: Property dictionary. It is not clobbered if a jump to the miss
89 // name: Property name. It is not clobbered if a jump to the miss label is
91 // value: The value to store (never clobbered).
93 // The generated code assumes that the receiver has slow properties,
94 // is not a global object and does not have interceptors.
95 static void GenerateDictionaryStore(MacroAssembler* masm,
102 DCHECK(!AreAliased(elements, name, value, scratch1, scratch2));
106 // Probe the dictionary.
107 NameDictionaryLookupStub::GeneratePositiveLookup(masm,
115 // If probing finds an entry in the dictionary check that the value
116 // is a normal property that is not read only.
119 static const int kElementsStartOffset = NameDictionary::kHeaderSize +
120 NameDictionary::kElementsStartIndex * kPointerSize;
121 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
122 static const int kTypeAndReadOnlyMask =
123 PropertyDetails::TypeField::kMask |
124 PropertyDetails::AttributesField::encode(READ_ONLY);
125 __ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset));
126 __ Tst(scratch1, kTypeAndReadOnlyMask);
129 // Store the value at the masked, scaled index and return.
130 static const int kValueOffset = kElementsStartOffset + kPointerSize;
131 __ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag);
132 __ Str(value, MemOperand(scratch2));
134 // Update the write barrier. Make sure not to clobber the value.
135 __ Mov(scratch1, value);
137 elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs);
141 // Checks the receiver for special cases (value type, slow case bits).
142 // Falls through for regular JS object and return the map of the
143 // receiver in 'map_scratch' if the receiver is not a SMI.
144 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
146 Register map_scratch,
150 DCHECK(!AreAliased(map_scratch, scratch));
152 // Check that the object isn't a smi.
153 __ JumpIfSmi(receiver, slow);
154 // Get the map of the receiver.
155 __ Ldr(map_scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
157 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kBitFieldOffset));
158 __ Tbnz(scratch, Map::kIsAccessCheckNeeded, slow);
159 __ Tbnz(scratch, interceptor_bit, slow);
161 // Check that the object is some kind of JS object EXCEPT JS Value type.
162 // In the case that the object is a value-wrapper object, we enter the
163 // runtime system to make sure that indexing into string objects work
165 STATIC_ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
166 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset));
167 __ Cmp(scratch, JS_OBJECT_TYPE);
172 // Loads an indexed element from a fast case array.
173 // If not_fast_array is NULL, doesn't perform the elements map check.
175 // receiver - holds the receiver on entry.
176 // Unchanged unless 'result' is the same register.
178 // key - holds the smi key on entry.
179 // Unchanged unless 'result' is the same register.
181 // elements - holds the elements of the receiver on exit.
183 // elements_map - holds the elements map on exit if the not_fast_array branch is
184 // taken. Otherwise, this is used as a scratch register.
186 // result - holds the result on exit if the load succeeded.
187 // Allowed to be the the same as 'receiver' or 'key'.
188 // Unchanged on bailout so 'receiver' and 'key' can be safely
189 // used by further computation.
190 static void GenerateFastArrayLoad(MacroAssembler* masm,
194 Register elements_map,
197 Label* not_fast_array,
199 DCHECK(!AreAliased(receiver, key, elements, elements_map, scratch2));
201 // Check for fast array.
202 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
203 if (not_fast_array != NULL) {
204 // Check that the object is in fast mode and writable.
205 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
206 __ JumpIfNotRoot(elements_map, Heap::kFixedArrayMapRootIndex,
209 __ AssertFastElements(elements);
212 // The elements_map register is only used for the not_fast_array path, which
213 // was handled above. From this point onward it is a scratch register.
214 Register scratch1 = elements_map;
216 // Check that the key (index) is within bounds.
217 __ Ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
218 __ Cmp(key, scratch1);
221 // Fast case: Do the load.
222 __ Add(scratch1, elements, FixedArray::kHeaderSize - kHeapObjectTag);
223 __ SmiUntag(scratch2, key);
224 __ Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2));
226 // In case the loaded value is the_hole we have to consult GetProperty
227 // to ensure the prototype chain is searched.
228 __ JumpIfRoot(scratch2, Heap::kTheHoleValueRootIndex, slow);
230 // Move the value to the result register.
231 // 'result' can alias with 'receiver' or 'key' but these two must be
232 // preserved if we jump to 'slow'.
233 __ Mov(result, scratch2);
237 // Checks whether a key is an array index string or a unique name.
238 // Falls through if a key is a unique name.
239 // The map of the key is returned in 'map_scratch'.
240 // If the jump to 'index_string' is done the hash of the key is left
241 // in 'hash_scratch'.
242 static void GenerateKeyNameCheck(MacroAssembler* masm,
244 Register map_scratch,
245 Register hash_scratch,
248 DCHECK(!AreAliased(key, map_scratch, hash_scratch));
250 // Is the key a name?
252 __ JumpIfObjectType(key, map_scratch, hash_scratch, LAST_UNIQUE_NAME_TYPE,
254 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
257 // Is the string an array index with cached numeric value?
258 __ Ldr(hash_scratch.W(), FieldMemOperand(key, Name::kHashFieldOffset));
259 __ TestAndBranchIfAllClear(hash_scratch,
260 Name::kContainsCachedArrayIndexMask,
263 // Is the string internalized? We know it's a string, so a single bit test is
265 __ Ldrb(hash_scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset));
266 STATIC_ASSERT(kInternalizedTag == 0);
267 __ TestAndBranchIfAnySet(hash_scratch, kIsNotInternalizedMask, not_unique);
270 // Fall through if the key is a unique name.
274 // Neither 'object' nor 'key' are modified by this function.
276 // If the 'unmapped_case' or 'slow_case' exit is taken, the 'map' register is
277 // left with the object's elements map. Otherwise, it is used as a scratch
279 static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm,
285 Label* unmapped_case,
287 DCHECK(!AreAliased(object, key, map, scratch1, scratch2));
289 Heap* heap = masm->isolate()->heap();
291 // Check that the receiver is a JSObject. Because of the elements
292 // map check later, we do not need to check for interceptors or
293 // whether it requires access checks.
294 __ JumpIfSmi(object, slow_case);
295 // Check that the object is some kind of JSObject.
296 __ JumpIfObjectType(object, map, scratch1, FIRST_JS_RECEIVER_TYPE,
299 // Check that the key is a positive smi.
300 __ JumpIfNotSmi(key, slow_case);
301 __ Tbnz(key, kXSignBit, slow_case);
303 // Load the elements object and check its map.
304 Handle<Map> arguments_map(heap->sloppy_arguments_elements_map());
305 __ Ldr(map, FieldMemOperand(object, JSObject::kElementsOffset));
306 __ CheckMap(map, scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK);
308 // Check if element is in the range of mapped arguments. If not, jump
309 // to the unmapped lookup.
310 __ Ldr(scratch1, FieldMemOperand(map, FixedArray::kLengthOffset));
311 __ Sub(scratch1, scratch1, Smi::FromInt(2));
312 __ Cmp(key, scratch1);
313 __ B(hs, unmapped_case);
315 // Load element index and check whether it is the hole.
316 static const int offset =
317 FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag;
319 __ Add(scratch1, map, offset);
320 __ SmiUntag(scratch2, key);
321 __ Ldr(scratch1, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2));
322 __ JumpIfRoot(scratch1, Heap::kTheHoleValueRootIndex, unmapped_case);
324 // Load value from context and return it.
325 __ Ldr(scratch2, FieldMemOperand(map, FixedArray::kHeaderSize));
326 __ SmiUntag(scratch1);
327 __ Lsl(scratch1, scratch1, kPointerSizeLog2);
328 __ Add(scratch1, scratch1, Context::kHeaderSize - kHeapObjectTag);
329 // The base of the result (scratch2) is passed to RecordWrite in
330 // KeyedStoreIC::GenerateSloppyArguments and it must be a HeapObject.
331 return MemOperand(scratch2, scratch1);
335 // The 'parameter_map' register must be loaded with the parameter map of the
336 // arguments object and is overwritten.
337 static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
339 Register parameter_map,
342 DCHECK(!AreAliased(key, parameter_map, scratch));
344 // Element is in arguments backing store, which is referenced by the
345 // second element of the parameter_map.
346 const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
347 Register backing_store = parameter_map;
348 __ Ldr(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset));
349 Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map());
351 backing_store, scratch, fixed_array_map, slow_case, DONT_DO_SMI_CHECK);
352 __ Ldr(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset));
353 __ Cmp(key, scratch);
356 __ Add(backing_store,
358 FixedArray::kHeaderSize - kHeapObjectTag);
359 __ SmiUntag(scratch, key);
360 return MemOperand(backing_store, scratch, LSL, kPointerSizeLog2);
364 void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
365 // The return address is in lr.
366 Register receiver = ReceiverRegister();
367 Register name = NameRegister();
368 DCHECK(receiver.is(x1));
371 // Probe the stub cache.
372 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
373 Code::ComputeHandlerFlags(Code::LOAD_IC));
374 masm->isolate()->stub_cache()->GenerateProbe(
375 masm, flags, receiver, name, x3, x4, x5, x6);
377 // Cache miss: Jump to runtime.
382 void LoadIC::GenerateNormal(MacroAssembler* masm) {
383 Register dictionary = x0;
384 DCHECK(!dictionary.is(ReceiverRegister()));
385 DCHECK(!dictionary.is(NameRegister()));
389 FieldMemOperand(ReceiverRegister(), JSObject::kPropertiesOffset));
390 GenerateDictionaryLoad(masm, &slow, dictionary, NameRegister(), x0, x3, x4);
393 // Dictionary load failed, go slow (but don't miss).
395 GenerateRuntimeGetProperty(masm);
399 void LoadIC::GenerateMiss(MacroAssembler* masm) {
400 // The return address is in lr.
401 Isolate* isolate = masm->isolate();
402 ASM_LOCATION("LoadIC::GenerateMiss");
404 __ IncrementCounter(isolate->counters()->load_miss(), 1, x3, x4);
406 // Perform tail call to the entry.
407 __ Push(ReceiverRegister(), NameRegister());
408 ExternalReference ref =
409 ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
410 __ TailCallExternalReference(ref, 2, 1);
414 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
415 // The return address is in lr.
416 __ Push(ReceiverRegister(), NameRegister());
417 __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
421 void KeyedLoadIC::GenerateSloppyArguments(MacroAssembler* masm) {
422 // The return address is in lr.
423 Register result = x0;
424 Register receiver = ReceiverRegister();
425 Register key = NameRegister();
426 DCHECK(receiver.is(x1));
429 Label miss, unmapped;
431 Register map_scratch = x0;
432 MemOperand mapped_location = GenerateMappedArgumentsLookup(
433 masm, receiver, key, map_scratch, x3, x4, &unmapped, &miss);
434 __ Ldr(result, mapped_location);
438 // Parameter map is left in map_scratch when a jump on unmapped is done.
439 MemOperand unmapped_location =
440 GenerateUnmappedArgumentsLookup(masm, key, map_scratch, x3, &miss);
441 __ Ldr(result, unmapped_location);
442 __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss);
450 void KeyedStoreIC::GenerateSloppyArguments(MacroAssembler* masm) {
451 ASM_LOCATION("KeyedStoreIC::GenerateSloppyArguments");
453 Register value = ValueRegister();
454 Register key = NameRegister();
455 Register receiver = ReceiverRegister();
456 DCHECK(receiver.is(x1));
458 DCHECK(value.is(x0));
462 // These registers are used by GenerateMappedArgumentsLookup to build a
463 // MemOperand. They are live for as long as the MemOperand is live.
464 Register mapped1 = x4;
465 Register mapped2 = x5;
468 GenerateMappedArgumentsLookup(masm, receiver, key, map,
471 Operand mapped_offset = mapped.OffsetAsOperand();
472 __ Str(value, mapped);
473 __ Add(x10, mapped.base(), mapped_offset);
475 __ RecordWrite(mapped.base(), x10, x11, kLRHasNotBeenSaved, kDontSaveFPRegs);
480 // These registers are used by GenerateMappedArgumentsLookup to build a
481 // MemOperand. They are live for as long as the MemOperand is live.
482 Register unmapped1 = map; // This is assumed to alias 'map'.
483 Register unmapped2 = x4;
484 MemOperand unmapped =
485 GenerateUnmappedArgumentsLookup(masm, key, unmapped1, unmapped2, &slow);
486 Operand unmapped_offset = unmapped.OffsetAsOperand();
487 __ Str(value, unmapped);
488 __ Add(x10, unmapped.base(), unmapped_offset);
490 __ RecordWrite(unmapped.base(), x10, x11,
491 kLRHasNotBeenSaved, kDontSaveFPRegs);
498 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
499 // The return address is in lr.
500 Isolate* isolate = masm->isolate();
502 __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, x10, x11);
504 __ Push(ReceiverRegister(), NameRegister());
506 // Perform tail call to the entry.
507 ExternalReference ref =
508 ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
510 __ TailCallExternalReference(ref, 2, 1);
514 // IC register specifications
515 const Register LoadIC::ReceiverRegister() { return x1; }
516 const Register LoadIC::NameRegister() { return x2; }
518 const Register LoadIC::SlotRegister() {
519 DCHECK(FLAG_vector_ics);
524 const Register LoadIC::VectorRegister() {
525 DCHECK(FLAG_vector_ics);
530 const Register StoreIC::ReceiverRegister() { return x1; }
531 const Register StoreIC::NameRegister() { return x2; }
532 const Register StoreIC::ValueRegister() { return x0; }
535 const Register KeyedStoreIC::MapRegister() {
540 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
541 // The return address is in lr.
542 __ Push(ReceiverRegister(), NameRegister());
543 __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
547 static void GenerateKeyedLoadWithSmiKey(MacroAssembler* masm,
557 key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5));
559 Isolate* isolate = masm->isolate();
560 Label check_number_dictionary;
561 // If we can load the value, it should be returned in x0.
562 Register result = x0;
564 GenerateKeyedLoadReceiverCheck(
565 masm, receiver, scratch1, scratch2, Map::kHasIndexedInterceptor, slow);
567 // Check the receiver's map to see if it has fast elements.
568 __ CheckFastElements(scratch1, scratch2, &check_number_dictionary);
570 GenerateFastArrayLoad(
571 masm, receiver, key, scratch3, scratch2, scratch1, result, NULL, slow);
573 isolate->counters()->keyed_load_generic_smi(), 1, scratch1, scratch2);
576 __ Bind(&check_number_dictionary);
577 __ Ldr(scratch3, FieldMemOperand(receiver, JSObject::kElementsOffset));
578 __ Ldr(scratch2, FieldMemOperand(scratch3, JSObject::kMapOffset));
580 // Check whether we have a number dictionary.
581 __ JumpIfNotRoot(scratch2, Heap::kHashTableMapRootIndex, slow);
583 __ LoadFromNumberDictionary(
584 slow, scratch3, key, result, scratch1, scratch2, scratch4, scratch5);
588 static void GenerateKeyedLoadWithNameKey(MacroAssembler* masm,
598 key, receiver, scratch1, scratch2, scratch3, scratch4, scratch5));
600 Isolate* isolate = masm->isolate();
601 Label probe_dictionary, property_array_property;
602 // If we can load the value, it should be returned in x0.
603 Register result = x0;
605 GenerateKeyedLoadReceiverCheck(
606 masm, receiver, scratch1, scratch2, Map::kHasNamedInterceptor, slow);
608 // If the receiver is a fast-case object, check the keyed lookup cache.
609 // Otherwise probe the dictionary.
610 __ Ldr(scratch2, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
611 __ Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset));
612 __ JumpIfRoot(scratch3, Heap::kHashTableMapRootIndex, &probe_dictionary);
614 // We keep the map of the receiver in scratch1.
615 Register receiver_map = scratch1;
617 // Load the map of the receiver, compute the keyed lookup cache hash
618 // based on 32 bits of the map pointer and the name hash.
619 __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
620 __ Mov(scratch2, Operand(receiver_map, ASR, KeyedLookupCache::kMapHashShift));
621 __ Ldr(scratch3.W(), FieldMemOperand(key, Name::kHashFieldOffset));
622 __ Eor(scratch2, scratch2, Operand(scratch3, ASR, Name::kHashShift));
623 int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask;
624 __ And(scratch2, scratch2, mask);
626 // Load the key (consisting of map and unique name) from the cache and
628 Label load_in_object_property;
629 static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
630 Label hit_on_nth_entry[kEntriesPerBucket];
631 ExternalReference cache_keys =
632 ExternalReference::keyed_lookup_cache_keys(isolate);
634 __ Mov(scratch3, cache_keys);
635 __ Add(scratch3, scratch3, Operand(scratch2, LSL, kPointerSizeLog2 + 1));
637 for (int i = 0; i < kEntriesPerBucket - 1; i++) {
638 Label try_next_entry;
639 // Load map and make scratch3 pointing to the next entry.
640 __ Ldr(scratch4, MemOperand(scratch3, kPointerSize * 2, PostIndex));
641 __ Cmp(receiver_map, scratch4);
642 __ B(ne, &try_next_entry);
643 __ Ldr(scratch4, MemOperand(scratch3, -kPointerSize)); // Load name
644 __ Cmp(key, scratch4);
645 __ B(eq, &hit_on_nth_entry[i]);
646 __ Bind(&try_next_entry);
650 __ Ldr(scratch4, MemOperand(scratch3, kPointerSize, PostIndex));
651 __ Cmp(receiver_map, scratch4);
653 __ Ldr(scratch4, MemOperand(scratch3));
654 __ Cmp(key, scratch4);
658 ExternalReference cache_field_offsets =
659 ExternalReference::keyed_lookup_cache_field_offsets(isolate);
662 for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
663 __ Bind(&hit_on_nth_entry[i]);
664 __ Mov(scratch3, cache_field_offsets);
666 __ Add(scratch2, scratch2, i);
668 __ Ldr(scratch4.W(), MemOperand(scratch3, scratch2, LSL, 2));
670 FieldMemOperand(receiver_map, Map::kInObjectPropertiesOffset));
671 __ Subs(scratch4, scratch4, scratch5);
672 __ B(ge, &property_array_property);
674 __ B(&load_in_object_property);
678 // Load in-object property.
679 __ Bind(&load_in_object_property);
680 __ Ldrb(scratch5, FieldMemOperand(receiver_map, Map::kInstanceSizeOffset));
681 __ Add(scratch5, scratch5, scratch4); // Index from start of object.
682 __ Sub(receiver, receiver, kHeapObjectTag); // Remove the heap tag.
683 __ Ldr(result, MemOperand(receiver, scratch5, LSL, kPointerSizeLog2));
684 __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
685 1, scratch1, scratch2);
688 // Load property array property.
689 __ Bind(&property_array_property);
690 __ Ldr(scratch1, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
691 __ Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag);
692 __ Ldr(result, MemOperand(scratch1, scratch4, LSL, kPointerSizeLog2));
693 __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
694 1, scratch1, scratch2);
697 // Do a quick inline probe of the receiver's dictionary, if it exists.
698 __ Bind(&probe_dictionary);
699 __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
700 __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
701 GenerateGlobalInstanceTypeCheck(masm, scratch1, slow);
702 // Load the property.
703 GenerateDictionaryLoad(masm, slow, scratch2, key, result, scratch1, scratch3);
704 __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(),
705 1, scratch1, scratch2);
710 void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
711 // The return address is in lr.
712 Label slow, check_name, index_smi, index_name;
714 Register key = NameRegister();
715 Register receiver = ReceiverRegister();
717 DCHECK(receiver.is(x1));
719 __ JumpIfNotSmi(key, &check_name);
721 // Now the key is known to be a smi. This place is also jumped to from below
722 // where a numeric string is converted to a smi.
723 GenerateKeyedLoadWithSmiKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow);
728 masm->isolate()->counters()->keyed_load_generic_slow(), 1, x4, x3);
729 GenerateRuntimeGetProperty(masm);
731 __ Bind(&check_name);
732 GenerateKeyNameCheck(masm, key, x0, x3, &index_name, &slow);
734 GenerateKeyedLoadWithNameKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow);
736 __ Bind(&index_name);
737 __ IndexFromHash(x3, key);
738 // Now jump to the place where smi keys are handled.
743 void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
744 // Return address is in lr.
747 Register receiver = ReceiverRegister();
748 Register index = NameRegister();
749 Register result = x0;
750 Register scratch = x3;
751 DCHECK(!scratch.is(receiver) && !scratch.is(index));
753 StringCharAtGenerator char_at_generator(receiver,
757 &miss, // When not a string.
758 &miss, // When not a number.
759 &miss, // When index out of range.
760 STRING_INDEX_IS_ARRAY_INDEX);
761 char_at_generator.GenerateFast(masm);
764 StubRuntimeCallHelper call_helper;
765 char_at_generator.GenerateSlow(masm, call_helper);
772 void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
773 // Return address is in lr.
776 Register receiver = ReceiverRegister();
777 Register key = NameRegister();
778 Register scratch1 = x3;
779 Register scratch2 = x4;
780 DCHECK(!AreAliased(scratch1, scratch2, receiver, key));
782 // Check that the receiver isn't a smi.
783 __ JumpIfSmi(receiver, &slow);
785 // Check that the key is an array index, that is Uint32.
786 __ TestAndBranchIfAnySet(key, kSmiTagMask | kSmiSignMask, &slow);
788 // Get the map of the receiver.
789 Register map = scratch1;
790 __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
792 // Check that it has indexed interceptor and access checks
793 // are not enabled for this object.
794 __ Ldrb(scratch2, FieldMemOperand(map, Map::kBitFieldOffset));
795 DCHECK(kSlowCaseBitFieldMask ==
796 ((1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor)));
797 __ Tbnz(scratch2, Map::kIsAccessCheckNeeded, &slow);
798 __ Tbz(scratch2, Map::kHasIndexedInterceptor, &slow);
800 // Everything is fine, call runtime.
801 __ Push(receiver, key);
802 __ TailCallExternalReference(
803 ExternalReference(IC_Utility(kLoadElementWithInterceptor),
812 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
813 ASM_LOCATION("KeyedStoreIC::GenerateMiss");
815 // Push receiver, key and value for runtime call.
816 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
818 ExternalReference ref =
819 ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
820 __ TailCallExternalReference(ref, 3, 1);
824 void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
825 ASM_LOCATION("KeyedStoreIC::GenerateSlow");
827 // Push receiver, key and value for runtime call.
828 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
830 // The slow case calls into the runtime to complete the store without causing
831 // an IC miss that would otherwise cause a transition to the generic stub.
832 ExternalReference ref =
833 ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
834 __ TailCallExternalReference(ref, 3, 1);
838 void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
839 StrictMode strict_mode) {
840 ASM_LOCATION("KeyedStoreIC::GenerateRuntimeSetProperty");
842 // Push receiver, key and value for runtime call.
843 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
845 // Push strict_mode for runtime call.
846 __ Mov(x10, Smi::FromInt(strict_mode));
849 __ TailCallRuntime(Runtime::kSetProperty, 4, 1);
853 static void KeyedStoreGenerateGenericHelper(
854 MacroAssembler* masm,
858 KeyedStoreCheckMap check_map,
859 KeyedStoreIncrementLength increment_length,
863 Register receiver_map,
864 Register elements_map,
867 value, key, receiver, receiver_map, elements_map, elements, x10, x11));
869 Label transition_smi_elements;
870 Label transition_double_elements;
871 Label fast_double_without_map_check;
872 Label non_double_value;
875 __ Bind(fast_object);
876 if (check_map == kCheckMap) {
877 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
879 Operand(masm->isolate()->factory()->fixed_array_map()));
880 __ B(ne, fast_double);
883 // HOLECHECK: guards "A[i] = V"
884 // We have to go to the runtime if the current value is the hole because there
885 // may be a callback on the element.
886 Label holecheck_passed;
887 __ Add(x10, elements, FixedArray::kHeaderSize - kHeapObjectTag);
888 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
889 __ Ldr(x11, MemOperand(x10));
890 __ JumpIfNotRoot(x11, Heap::kTheHoleValueRootIndex, &holecheck_passed);
891 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow);
892 __ bind(&holecheck_passed);
894 // Smi stores don't require further checks.
895 __ JumpIfSmi(value, &finish_store);
897 // Escape to elements kind transition case.
898 __ CheckFastObjectElements(receiver_map, x10, &transition_smi_elements);
900 __ Bind(&finish_store);
901 if (increment_length == kIncrementLength) {
902 // Add 1 to receiver->length.
903 __ Add(x10, key, Smi::FromInt(1));
904 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset));
907 Register address = x11;
908 __ Add(address, elements, FixedArray::kHeaderSize - kHeapObjectTag);
909 __ Add(address, address, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
910 __ Str(value, MemOperand(address));
912 Label dont_record_write;
913 __ JumpIfSmi(value, &dont_record_write);
915 // Update write barrier for the elements array address.
916 __ Mov(x10, value); // Preserve the value which is returned.
917 __ RecordWrite(elements,
925 __ Bind(&dont_record_write);
929 __ Bind(fast_double);
930 if (check_map == kCheckMap) {
931 // Check for fast double array case. If this fails, call through to the
933 __ JumpIfNotRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex, slow);
936 // HOLECHECK: guards "A[i] double hole?"
937 // We have to see if the double version of the hole is present. If so go to
939 __ Add(x10, elements, FixedDoubleArray::kHeaderSize - kHeapObjectTag);
940 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
941 __ Ldr(x11, MemOperand(x10));
942 __ CompareAndBranch(x11, kHoleNanInt64, ne, &fast_double_without_map_check);
943 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow);
945 __ Bind(&fast_double_without_map_check);
946 __ StoreNumberToDoubleElements(value,
951 &transition_double_elements);
952 if (increment_length == kIncrementLength) {
953 // Add 1 to receiver->length.
954 __ Add(x10, key, Smi::FromInt(1));
955 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset));
960 __ Bind(&transition_smi_elements);
961 // Transition the array appropriately depending on the value type.
962 __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset));
963 __ JumpIfNotRoot(x10, Heap::kHeapNumberMapRootIndex, &non_double_value);
965 // Value is a double. Transition FAST_SMI_ELEMENTS ->
966 // FAST_DOUBLE_ELEMENTS and complete the store.
967 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
968 FAST_DOUBLE_ELEMENTS,
973 AllocationSiteMode mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS,
974 FAST_DOUBLE_ELEMENTS);
975 ElementsTransitionGenerator::GenerateSmiToDouble(
976 masm, receiver, key, value, receiver_map, mode, slow);
977 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
978 __ B(&fast_double_without_map_check);
980 __ Bind(&non_double_value);
981 // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS.
982 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
989 mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
990 ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
991 masm, receiver, key, value, receiver_map, mode, slow);
993 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
996 __ Bind(&transition_double_elements);
997 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
998 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
999 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
1000 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS,
1006 mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
1007 ElementsTransitionGenerator::GenerateDoubleToObject(
1008 masm, receiver, key, value, receiver_map, mode, slow);
1009 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
1010 __ B(&finish_store);
1014 void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
1015 StrictMode strict_mode) {
1016 ASM_LOCATION("KeyedStoreIC::GenerateGeneric");
1021 Label fast_object_grow;
1022 Label fast_double_grow;
1025 Register value = ValueRegister();
1026 Register key = NameRegister();
1027 Register receiver = ReceiverRegister();
1028 DCHECK(receiver.is(x1));
1030 DCHECK(value.is(x0));
1032 Register receiver_map = x3;
1033 Register elements = x4;
1034 Register elements_map = x5;
1036 __ JumpIfNotSmi(key, &slow);
1037 __ JumpIfSmi(receiver, &slow);
1038 __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
1040 // Check that the receiver does not require access checks and is not observed.
1041 // The generic stub does not perform map checks or handle observed objects.
1042 __ Ldrb(x10, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
1043 __ TestAndBranchIfAnySet(
1044 x10, (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kIsObserved), &slow);
1046 // Check if the object is a JS array or not.
1047 Register instance_type = x10;
1048 __ CompareInstanceType(receiver_map, instance_type, JS_ARRAY_TYPE);
1050 // Check that the object is some kind of JSObject.
1051 __ Cmp(instance_type, FIRST_JS_OBJECT_TYPE);
1054 // Object case: Check key against length in the elements array.
1055 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
1056 // Check array bounds. Both the key and the length of FixedArray are smis.
1057 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset));
1058 __ Cmp(x10, Operand::UntagSmi(key));
1059 __ B(hi, &fast_object);
1063 // Slow case, handle jump to runtime.
1068 GenerateRuntimeSetProperty(masm, strict_mode);
1072 // Extra capacity case: Check if there is extra capacity to
1073 // perform the store and update the length. Used for adding one
1074 // element to the array by writing to array[array.length].
1076 // Check for room in the elements backing store.
1077 // Both the key and the length of FixedArray are smis.
1078 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset));
1079 __ Cmp(x10, Operand::UntagSmi(key));
1082 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
1083 __ Cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map()));
1084 __ B(eq, &fast_object_grow);
1085 __ Cmp(elements_map,
1086 Operand(masm->isolate()->factory()->fixed_double_array_map()));
1087 __ B(eq, &fast_double_grow);
1092 // Array case: Get the length and the elements array from the JS
1093 // array. Check that the array is in fast mode (and writable); if it
1094 // is the length is always a smi.
1096 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
1098 // Check the key against the length in the array.
1099 __ Ldrsw(x10, UntagSmiFieldMemOperand(receiver, JSArray::kLengthOffset));
1100 __ Cmp(x10, Operand::UntagSmi(key));
1101 __ B(eq, &extra); // We can handle the case where we are appending 1 element.
1104 KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
1105 &slow, kCheckMap, kDontIncrementLength,
1106 value, key, receiver, receiver_map,
1107 elements_map, elements);
1108 KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
1109 &slow, kDontCheckMap, kIncrementLength,
1110 value, key, receiver, receiver_map,
1111 elements_map, elements);
1115 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
1116 Register receiver = ReceiverRegister();
1117 Register name = NameRegister();
1118 DCHECK(!AreAliased(receiver, name, ValueRegister(), x3, x4, x5, x6));
1120 // Probe the stub cache.
1121 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
1122 Code::ComputeHandlerFlags(Code::STORE_IC));
1123 masm->isolate()->stub_cache()->GenerateProbe(
1124 masm, flags, receiver, name, x3, x4, x5, x6);
1126 // Cache miss: Jump to runtime.
1131 void StoreIC::GenerateMiss(MacroAssembler* masm) {
1132 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
1134 // Tail call to the entry.
1135 ExternalReference ref =
1136 ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
1137 __ TailCallExternalReference(ref, 3, 1);
1141 void StoreIC::GenerateNormal(MacroAssembler* masm) {
1143 Register value = ValueRegister();
1144 Register receiver = ReceiverRegister();
1145 Register name = NameRegister();
1146 Register dictionary = x3;
1147 DCHECK(!AreAliased(value, receiver, name, x3, x4, x5));
1149 __ Ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
1151 GenerateDictionaryStore(masm, &miss, dictionary, name, value, x4, x5);
1152 Counters* counters = masm->isolate()->counters();
1153 __ IncrementCounter(counters->store_normal_hit(), 1, x4, x5);
1156 // Cache miss: Jump to runtime.
1158 __ IncrementCounter(counters->store_normal_miss(), 1, x4, x5);
1163 void StoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
1164 StrictMode strict_mode) {
1165 ASM_LOCATION("StoreIC::GenerateRuntimeSetProperty");
1167 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
1169 __ Mov(x10, Smi::FromInt(strict_mode));
1172 // Do tail-call to runtime routine.
1173 __ TailCallRuntime(Runtime::kSetProperty, 4, 1);
1177 void StoreIC::GenerateSlow(MacroAssembler* masm) {
1178 // ---------- S t a t e --------------
1182 // -- lr : return address
1183 // -----------------------------------
1185 // Push receiver, name and value for runtime call.
1186 __ Push(ReceiverRegister(), NameRegister(), ValueRegister());
1188 // The slow case calls into the runtime to complete the store without causing
1189 // an IC miss that would otherwise cause a transition to the generic stub.
1190 ExternalReference ref =
1191 ExternalReference(IC_Utility(kStoreIC_Slow), masm->isolate());
1192 __ TailCallExternalReference(ref, 3, 1);
1196 Condition CompareIC::ComputeCondition(Token::Value op) {
1198 case Token::EQ_STRICT:
1216 bool CompareIC::HasInlinedSmiCode(Address address) {
1217 // The address of the instruction following the call.
1218 Address info_address =
1219 Assembler::return_address_from_call_start(address);
1221 InstructionSequence* patch_info = InstructionSequence::At(info_address);
1222 return patch_info->IsInlineData();
1226 // Activate a SMI fast-path by patching the instructions generated by
1227 // JumpPatchSite::EmitJumpIf(Not)Smi(), using the information encoded by
1228 // JumpPatchSite::EmitPatchInfo().
1229 void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
1230 // The patch information is encoded in the instruction stream using
1231 // instructions which have no side effects, so we can safely execute them.
1232 // The patch information is encoded directly after the call to the helper
1233 // function which is requesting this patch operation.
1234 Address info_address =
1235 Assembler::return_address_from_call_start(address);
1236 InlineSmiCheckInfo info(info_address);
1238 // Check and decode the patch information instruction.
1239 if (!info.HasSmiCheck()) {
1243 if (FLAG_trace_ic) {
1244 PrintF("[ Patching ic at %p, marker=%p, SMI check=%p\n",
1245 address, info_address, reinterpret_cast<void*>(info.SmiCheck()));
1248 // Patch and activate code generated by JumpPatchSite::EmitJumpIfNotSmi()
1249 // and JumpPatchSite::EmitJumpIfSmi().
1251 // tb(n)z xzr, #0, <target>
1253 // tb(!n)z test_reg, #0, <target>
1254 Instruction* to_patch = info.SmiCheck();
1255 PatchingAssembler patcher(to_patch, 1);
1256 DCHECK(to_patch->IsTestBranch());
1257 DCHECK(to_patch->ImmTestBranchBit5() == 0);
1258 DCHECK(to_patch->ImmTestBranchBit40() == 0);
1260 STATIC_ASSERT(kSmiTag == 0);
1261 STATIC_ASSERT(kSmiTagMask == 1);
1263 int branch_imm = to_patch->ImmTestBranch();
1265 if (check == ENABLE_INLINED_SMI_CHECK) {
1266 DCHECK(to_patch->Rt() == xzr.code());
1267 smi_reg = info.SmiRegister();
1269 DCHECK(check == DISABLE_INLINED_SMI_CHECK);
1270 DCHECK(to_patch->Rt() != xzr.code());
1274 if (to_patch->Mask(TestBranchMask) == TBZ) {
1275 // This is JumpIfNotSmi(smi_reg, branch_imm).
1276 patcher.tbnz(smi_reg, 0, branch_imm);
1278 DCHECK(to_patch->Mask(TestBranchMask) == TBNZ);
1279 // This is JumpIfSmi(smi_reg, branch_imm).
1280 patcher.tbz(smi_reg, 0, branch_imm);
1285 } } // namespace v8::internal
1287 #endif // V8_TARGET_ARCH_ARM64