blob: eb933c78ec970db33f59c876b216933814da0be3 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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.
4
Ben Murdochb8a8cc12014-11-26 15:28:44 +00005#if V8_TARGET_ARCH_ARM64
6
7#include "src/codegen.h"
8#include "src/ic/ic.h"
9#include "src/ic/ic-compiler.h"
10#include "src/ic/stub-cache.h"
11
12namespace v8 {
13namespace internal {
14
15
16#define __ ACCESS_MASM(masm)
17
18
19// "type" holds an instance type on entry and is not clobbered.
20// Generated code branch on "global_object" if type is any kind of global
21// JS object.
22static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type,
23 Label* global_object) {
24 __ Cmp(type, JS_GLOBAL_OBJECT_TYPE);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000025 __ Ccmp(type, JS_GLOBAL_PROXY_TYPE, ZFlag, ne);
26 __ B(eq, global_object);
27}
28
29
30// Helper function used from LoadIC GenerateNormal.
31//
32// elements: Property dictionary. It is not clobbered if a jump to the miss
33// label is done.
34// name: Property name. It is not clobbered if a jump to the miss label is
35// done
36// result: Register for the result. It is only updated if a jump to the miss
37// label is not done.
38// The scratch registers need to be different from elements, name and result.
39// The generated code assumes that the receiver has slow properties,
40// is not a global object and does not have interceptors.
41static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
42 Register elements, Register name,
43 Register result, Register scratch1,
44 Register scratch2) {
45 DCHECK(!AreAliased(elements, name, scratch1, scratch2));
46 DCHECK(!AreAliased(result, scratch1, scratch2));
47
48 Label done;
49
50 // Probe the dictionary.
51 NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
52 name, scratch1, scratch2);
53
54 // If probing finds an entry check that the value is a normal property.
55 __ Bind(&done);
56
57 static const int kElementsStartOffset =
58 NameDictionary::kHeaderSize +
59 NameDictionary::kElementsStartIndex * kPointerSize;
60 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
61 __ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
62 __ Tst(scratch1, Smi::FromInt(PropertyDetails::TypeField::kMask));
63 __ B(ne, miss);
64
65 // Get the value at the masked, scaled index and return.
66 __ Ldr(result,
67 FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
68}
69
70
71// Helper function used from StoreIC::GenerateNormal.
72//
73// elements: Property dictionary. It is not clobbered if a jump to the miss
74// label is done.
75// name: Property name. It is not clobbered if a jump to the miss label is
76// done
77// value: The value to store (never clobbered).
78//
79// The generated code assumes that the receiver has slow properties,
80// is not a global object and does not have interceptors.
81static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
82 Register elements, Register name,
83 Register value, Register scratch1,
84 Register scratch2) {
85 DCHECK(!AreAliased(elements, name, value, scratch1, scratch2));
86
87 Label done;
88
89 // Probe the dictionary.
90 NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
91 name, scratch1, scratch2);
92
93 // If probing finds an entry in the dictionary check that the value
94 // is a normal property that is not read only.
95 __ Bind(&done);
96
97 static const int kElementsStartOffset =
98 NameDictionary::kHeaderSize +
99 NameDictionary::kElementsStartIndex * kPointerSize;
100 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
101 static const int kTypeAndReadOnlyMask =
102 PropertyDetails::TypeField::kMask |
103 PropertyDetails::AttributesField::encode(READ_ONLY);
104 __ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset));
105 __ Tst(scratch1, kTypeAndReadOnlyMask);
106 __ B(ne, miss);
107
108 // Store the value at the masked, scaled index and return.
109 static const int kValueOffset = kElementsStartOffset + kPointerSize;
110 __ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag);
111 __ Str(value, MemOperand(scratch2));
112
113 // Update the write barrier. Make sure not to clobber the value.
114 __ Mov(scratch1, value);
115 __ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
116 kDontSaveFPRegs);
117}
118
119
120// Checks the receiver for special cases (value type, slow case bits).
121// Falls through for regular JS object and return the map of the
122// receiver in 'map_scratch' if the receiver is not a SMI.
123static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
124 Register receiver,
125 Register map_scratch,
126 Register scratch,
127 int interceptor_bit, Label* slow) {
128 DCHECK(!AreAliased(map_scratch, scratch));
129
130 // Check that the object isn't a smi.
131 __ JumpIfSmi(receiver, slow);
132 // Get the map of the receiver.
133 __ Ldr(map_scratch, FieldMemOperand(receiver, HeapObject::kMapOffset));
134 // Check bit field.
135 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kBitFieldOffset));
136 __ Tbnz(scratch, Map::kIsAccessCheckNeeded, slow);
137 __ Tbnz(scratch, interceptor_bit, slow);
138
139 // Check that the object is some kind of JS object EXCEPT JS Value type.
140 // In the case that the object is a value-wrapper object, we enter the
141 // runtime system to make sure that indexing into string objects work
142 // as intended.
143 STATIC_ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
144 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset));
145 __ Cmp(scratch, JS_OBJECT_TYPE);
146 __ B(lt, slow);
147}
148
149
150// Loads an indexed element from a fast case array.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000151//
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000152// receiver - holds the receiver on entry.
153// Unchanged unless 'result' is the same register.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000154//
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000155// key - holds the smi key on entry.
156// Unchanged unless 'result' is the same register.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000157//
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000158// elements - holds the elements of the receiver and its prototypes. Clobbered.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000159//
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000160// result - holds the result on exit if the load succeeded.
161// Allowed to be the the same as 'receiver' or 'key'.
162// Unchanged on bailout so 'receiver' and 'key' can be safely
163// used by further computation.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000164static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver,
165 Register key, Register elements,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000166 Register scratch1, Register scratch2,
167 Register result, Label* slow,
168 LanguageMode language_mode) {
169 DCHECK(!AreAliased(receiver, key, elements, scratch1, scratch2));
170
171 Label check_prototypes, check_next_prototype;
172 Label done, in_bounds, absent;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000173
174 // Check for fast array.
175 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000176 __ AssertFastElements(elements);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000177
178 // Check that the key (index) is within bounds.
179 __ Ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
180 __ Cmp(key, scratch1);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000181 __ B(lo, &in_bounds);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000182
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000183 // Out of bounds. Check the prototype chain to see if we can just return
184 // 'undefined'.
185 __ Cmp(key, Operand(Smi::FromInt(0)));
186 __ B(lt, slow); // Negative keys can't take the fast OOB path.
187 __ Bind(&check_prototypes);
188 __ Ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
189 __ Bind(&check_next_prototype);
190 __ Ldr(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset));
191 // scratch2: current prototype
192 __ JumpIfRoot(scratch2, Heap::kNullValueRootIndex, &absent);
193 __ Ldr(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset));
194 __ Ldr(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset));
195 // elements: elements of current prototype
196 // scratch2: map of current prototype
197 __ CompareInstanceType(scratch2, scratch1, JS_OBJECT_TYPE);
198 __ B(lo, slow);
199 __ Ldrb(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset));
200 __ Tbnz(scratch1, Map::kIsAccessCheckNeeded, slow);
201 __ Tbnz(scratch1, Map::kHasIndexedInterceptor, slow);
202 __ JumpIfNotRoot(elements, Heap::kEmptyFixedArrayRootIndex, slow);
203 __ B(&check_next_prototype);
204
205 __ Bind(&absent);
206 if (is_strong(language_mode)) {
207 // Strong mode accesses must throw in this case, so call the runtime.
208 __ B(slow);
209 } else {
210 __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
211 __ B(&done);
212 }
213
214 __ Bind(&in_bounds);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000215 // Fast case: Do the load.
216 __ Add(scratch1, elements, FixedArray::kHeaderSize - kHeapObjectTag);
217 __ SmiUntag(scratch2, key);
218 __ Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2));
219
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000220 // In case the loaded value is the_hole we have to check the prototype chain.
221 __ JumpIfRoot(scratch2, Heap::kTheHoleValueRootIndex, &check_prototypes);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000222
223 // Move the value to the result register.
224 // 'result' can alias with 'receiver' or 'key' but these two must be
225 // preserved if we jump to 'slow'.
226 __ Mov(result, scratch2);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000227 __ Bind(&done);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000228}
229
230
231// Checks whether a key is an array index string or a unique name.
232// Falls through if a key is a unique name.
233// The map of the key is returned in 'map_scratch'.
234// If the jump to 'index_string' is done the hash of the key is left
235// in 'hash_scratch'.
236static void GenerateKeyNameCheck(MacroAssembler* masm, Register key,
237 Register map_scratch, Register hash_scratch,
238 Label* index_string, Label* not_unique) {
239 DCHECK(!AreAliased(key, map_scratch, hash_scratch));
240
241 // Is the key a name?
242 Label unique;
243 __ JumpIfObjectType(key, map_scratch, hash_scratch, LAST_UNIQUE_NAME_TYPE,
244 not_unique, hi);
245 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
246 __ B(eq, &unique);
247
248 // Is the string an array index with cached numeric value?
249 __ Ldr(hash_scratch.W(), FieldMemOperand(key, Name::kHashFieldOffset));
250 __ TestAndBranchIfAllClear(hash_scratch, Name::kContainsCachedArrayIndexMask,
251 index_string);
252
253 // Is the string internalized? We know it's a string, so a single bit test is
254 // enough.
255 __ Ldrb(hash_scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset));
256 STATIC_ASSERT(kInternalizedTag == 0);
257 __ TestAndBranchIfAnySet(hash_scratch, kIsNotInternalizedMask, not_unique);
258
259 __ Bind(&unique);
260 // Fall through if the key is a unique name.
261}
262
263
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000264void LoadIC::GenerateNormal(MacroAssembler* masm, LanguageMode language_mode) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000265 Register dictionary = x0;
266 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
267 DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
268 Label slow;
269
270 __ Ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
271 JSObject::kPropertiesOffset));
272 GenerateDictionaryLoad(masm, &slow, dictionary,
273 LoadDescriptor::NameRegister(), x0, x3, x4);
274 __ Ret();
275
276 // Dictionary load failed, go slow (but don't miss).
277 __ Bind(&slow);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000278 GenerateRuntimeGetProperty(masm, language_mode);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000279}
280
281
282void LoadIC::GenerateMiss(MacroAssembler* masm) {
283 // The return address is in lr.
284 Isolate* isolate = masm->isolate();
285 ASM_LOCATION("LoadIC::GenerateMiss");
286
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000287 DCHECK(!AreAliased(x4, x5, LoadWithVectorDescriptor::SlotRegister(),
288 LoadWithVectorDescriptor::VectorRegister()));
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400289 __ IncrementCounter(isolate->counters()->load_miss(), 1, x4, x5);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000290
291 // Perform tail call to the entry.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000292 __ Push(LoadWithVectorDescriptor::ReceiverRegister(),
293 LoadWithVectorDescriptor::NameRegister(),
294 LoadWithVectorDescriptor::SlotRegister(),
295 LoadWithVectorDescriptor::VectorRegister());
296 __ TailCallRuntime(Runtime::kLoadIC_Miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000297}
298
299
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000300void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm,
301 LanguageMode language_mode) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000302 // The return address is in lr.
303 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000304
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000305 // Do tail-call to runtime routine.
306 __ TailCallRuntime(is_strong(language_mode) ? Runtime::kGetPropertyStrong
307 : Runtime::kGetProperty);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000308}
309
310
311void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
312 // The return address is in lr.
313 Isolate* isolate = masm->isolate();
314
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000315 DCHECK(!AreAliased(x10, x11, LoadWithVectorDescriptor::SlotRegister(),
316 LoadWithVectorDescriptor::VectorRegister()));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000317 __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, x10, x11);
318
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000319 __ Push(LoadWithVectorDescriptor::ReceiverRegister(),
320 LoadWithVectorDescriptor::NameRegister(),
321 LoadWithVectorDescriptor::SlotRegister(),
322 LoadWithVectorDescriptor::VectorRegister());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000323
324 // Perform tail call to the entry.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000325 __ TailCallRuntime(Runtime::kKeyedLoadIC_Miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000326}
327
328
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000329void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm,
330 LanguageMode language_mode) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000331 // The return address is in lr.
332 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000333
334 // Do tail-call to runtime routine.
335 __ TailCallRuntime(is_strong(language_mode) ? Runtime::kKeyedGetPropertyStrong
336 : Runtime::kKeyedGetProperty);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000337}
338
339
340static void GenerateKeyedLoadWithSmiKey(MacroAssembler* masm, Register key,
341 Register receiver, Register scratch1,
342 Register scratch2, Register scratch3,
343 Register scratch4, Register scratch5,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000344 Label* slow,
345 LanguageMode language_mode) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000346 DCHECK(!AreAliased(key, receiver, scratch1, scratch2, scratch3, scratch4,
347 scratch5));
348
349 Isolate* isolate = masm->isolate();
350 Label check_number_dictionary;
351 // If we can load the value, it should be returned in x0.
352 Register result = x0;
353
354 GenerateKeyedLoadReceiverCheck(masm, receiver, scratch1, scratch2,
355 Map::kHasIndexedInterceptor, slow);
356
357 // Check the receiver's map to see if it has fast elements.
358 __ CheckFastElements(scratch1, scratch2, &check_number_dictionary);
359
360 GenerateFastArrayLoad(masm, receiver, key, scratch3, scratch2, scratch1,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000361 result, slow, language_mode);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000362 __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1,
363 scratch1, scratch2);
364 __ Ret();
365
366 __ Bind(&check_number_dictionary);
367 __ Ldr(scratch3, FieldMemOperand(receiver, JSObject::kElementsOffset));
368 __ Ldr(scratch2, FieldMemOperand(scratch3, JSObject::kMapOffset));
369
370 // Check whether we have a number dictionary.
371 __ JumpIfNotRoot(scratch2, Heap::kHashTableMapRootIndex, slow);
372
373 __ LoadFromNumberDictionary(slow, scratch3, key, result, scratch1, scratch2,
374 scratch4, scratch5);
375 __ Ret();
376}
377
378static void GenerateKeyedLoadWithNameKey(MacroAssembler* masm, Register key,
379 Register receiver, Register scratch1,
380 Register scratch2, Register scratch3,
381 Register scratch4, Register scratch5,
382 Label* slow) {
383 DCHECK(!AreAliased(key, receiver, scratch1, scratch2, scratch3, scratch4,
384 scratch5));
385
386 Isolate* isolate = masm->isolate();
387 Label probe_dictionary, property_array_property;
388 // If we can load the value, it should be returned in x0.
389 Register result = x0;
390
391 GenerateKeyedLoadReceiverCheck(masm, receiver, scratch1, scratch2,
392 Map::kHasNamedInterceptor, slow);
393
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000394 // If the receiver is a fast-case object, check the stub cache. Otherwise
395 // probe the dictionary.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000396 __ Ldr(scratch2, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
397 __ Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset));
398 __ JumpIfRoot(scratch3, Heap::kHashTableMapRootIndex, &probe_dictionary);
399
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000400 // The handlers in the stub cache expect a vector and slot. Since we won't
401 // change the IC from any downstream misses, a dummy vector can be used.
402 Register vector = LoadWithVectorDescriptor::VectorRegister();
403 Register slot = LoadWithVectorDescriptor::SlotRegister();
404 DCHECK(!AreAliased(vector, slot, scratch1, scratch2, scratch3, scratch4));
405 Handle<TypeFeedbackVector> dummy_vector =
406 TypeFeedbackVector::DummyVector(masm->isolate());
407 int slot_index = dummy_vector->GetIndex(
408 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot));
409 __ LoadRoot(vector, Heap::kDummyVectorRootIndex);
410 __ Mov(slot, Operand(Smi::FromInt(slot_index)));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000411
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000412 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
413 Code::ComputeHandlerFlags(Code::LOAD_IC));
414 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::KEYED_LOAD_IC, flags,
415 receiver, key, scratch1,
416 scratch2, scratch3, scratch4);
417 // Cache miss.
418 KeyedLoadIC::GenerateMiss(masm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000419
420 // Do a quick inline probe of the receiver's dictionary, if it exists.
421 __ Bind(&probe_dictionary);
422 __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
423 __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
424 GenerateGlobalInstanceTypeCheck(masm, scratch1, slow);
425 // Load the property.
426 GenerateDictionaryLoad(masm, slow, scratch2, key, result, scratch1, scratch3);
427 __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(), 1,
428 scratch1, scratch2);
429 __ Ret();
430}
431
432
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000433void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm,
434 LanguageMode language_mode) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000435 // The return address is in lr.
436 Label slow, check_name, index_smi, index_name;
437
438 Register key = LoadDescriptor::NameRegister();
439 Register receiver = LoadDescriptor::ReceiverRegister();
440 DCHECK(key.is(x2));
441 DCHECK(receiver.is(x1));
442
443 __ JumpIfNotSmi(key, &check_name);
444 __ Bind(&index_smi);
445 // Now the key is known to be a smi. This place is also jumped to from below
446 // where a numeric string is converted to a smi.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000447 GenerateKeyedLoadWithSmiKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow,
448 language_mode);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000449
450 // Slow case.
451 __ Bind(&slow);
452 __ IncrementCounter(masm->isolate()->counters()->keyed_load_generic_slow(), 1,
453 x4, x3);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000454 GenerateRuntimeGetProperty(masm, language_mode);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000455
456 __ Bind(&check_name);
457 GenerateKeyNameCheck(masm, key, x0, x3, &index_name, &slow);
458
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000459 GenerateKeyedLoadWithNameKey(masm, key, receiver, x4, x5, x6, x7, x3, &slow);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000460
461 __ Bind(&index_name);
462 __ IndexFromHash(x3, key);
463 // Now jump to the place where smi keys are handled.
464 __ B(&index_smi);
465}
466
467
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000468static void StoreIC_PushArgs(MacroAssembler* masm) {
469 __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
470 StoreDescriptor::ValueRegister(),
471 VectorStoreICDescriptor::SlotRegister(),
472 VectorStoreICDescriptor::VectorRegister());
473}
474
475
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000476void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
477 ASM_LOCATION("KeyedStoreIC::GenerateMiss");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000478 StoreIC_PushArgs(masm);
479 __ TailCallRuntime(Runtime::kKeyedStoreIC_Miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000480}
481
482
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400483static void KeyedStoreGenerateMegamorphicHelper(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000484 MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
485 KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length,
486 Register value, Register key, Register receiver, Register receiver_map,
487 Register elements_map, Register elements) {
488 DCHECK(!AreAliased(value, key, receiver, receiver_map, elements_map, elements,
489 x10, x11));
490
491 Label transition_smi_elements;
492 Label transition_double_elements;
493 Label fast_double_without_map_check;
494 Label non_double_value;
495 Label finish_store;
496
497 __ Bind(fast_object);
498 if (check_map == kCheckMap) {
499 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
500 __ Cmp(elements_map,
501 Operand(masm->isolate()->factory()->fixed_array_map()));
502 __ B(ne, fast_double);
503 }
504
505 // HOLECHECK: guards "A[i] = V"
506 // We have to go to the runtime if the current value is the hole because there
507 // may be a callback on the element.
508 Label holecheck_passed;
509 __ Add(x10, elements, FixedArray::kHeaderSize - kHeapObjectTag);
510 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
511 __ Ldr(x11, MemOperand(x10));
512 __ JumpIfNotRoot(x11, Heap::kTheHoleValueRootIndex, &holecheck_passed);
513 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow);
514 __ bind(&holecheck_passed);
515
516 // Smi stores don't require further checks.
517 __ JumpIfSmi(value, &finish_store);
518
519 // Escape to elements kind transition case.
520 __ CheckFastObjectElements(receiver_map, x10, &transition_smi_elements);
521
522 __ Bind(&finish_store);
523 if (increment_length == kIncrementLength) {
524 // Add 1 to receiver->length.
525 __ Add(x10, key, Smi::FromInt(1));
526 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset));
527 }
528
529 Register address = x11;
530 __ Add(address, elements, FixedArray::kHeaderSize - kHeapObjectTag);
531 __ Add(address, address, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
532 __ Str(value, MemOperand(address));
533
534 Label dont_record_write;
535 __ JumpIfSmi(value, &dont_record_write);
536
537 // Update write barrier for the elements array address.
538 __ Mov(x10, value); // Preserve the value which is returned.
539 __ RecordWrite(elements, address, x10, kLRHasNotBeenSaved, kDontSaveFPRegs,
540 EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
541
542 __ Bind(&dont_record_write);
543 __ Ret();
544
545
546 __ Bind(fast_double);
547 if (check_map == kCheckMap) {
548 // Check for fast double array case. If this fails, call through to the
549 // runtime.
550 __ JumpIfNotRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex, slow);
551 }
552
553 // HOLECHECK: guards "A[i] double hole?"
554 // We have to see if the double version of the hole is present. If so go to
555 // the runtime.
556 __ Add(x10, elements, FixedDoubleArray::kHeaderSize - kHeapObjectTag);
557 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2));
558 __ Ldr(x11, MemOperand(x10));
559 __ CompareAndBranch(x11, kHoleNanInt64, ne, &fast_double_without_map_check);
560 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow);
561
562 __ Bind(&fast_double_without_map_check);
563 __ StoreNumberToDoubleElements(value, key, elements, x10, d0,
564 &transition_double_elements);
565 if (increment_length == kIncrementLength) {
566 // Add 1 to receiver->length.
567 __ Add(x10, key, Smi::FromInt(1));
568 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset));
569 }
570 __ Ret();
571
572
573 __ Bind(&transition_smi_elements);
574 // Transition the array appropriately depending on the value type.
575 __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset));
576 __ JumpIfNotRoot(x10, Heap::kHeapNumberMapRootIndex, &non_double_value);
577
578 // Value is a double. Transition FAST_SMI_ELEMENTS ->
579 // FAST_DOUBLE_ELEMENTS and complete the store.
580 __ LoadTransitionedArrayMapConditional(
581 FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, x10, x11, slow);
582 AllocationSiteMode mode =
583 AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS);
584 ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value,
585 receiver_map, mode, slow);
586 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
587 __ B(&fast_double_without_map_check);
588
589 __ Bind(&non_double_value);
590 // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS.
591 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS,
592 receiver_map, x10, x11, slow);
593
594 mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
595 ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
596 masm, receiver, key, value, receiver_map, mode, slow);
597
598 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
599 __ B(&finish_store);
600
601 __ Bind(&transition_double_elements);
602 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
603 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
604 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
605 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS,
606 receiver_map, x10, x11, slow);
607 mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
608 ElementsTransitionGenerator::GenerateDoubleToObject(
609 masm, receiver, key, value, receiver_map, mode, slow);
610 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
611 __ B(&finish_store);
612}
613
614
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400615void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000616 LanguageMode language_mode) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400617 ASM_LOCATION("KeyedStoreIC::GenerateMegamorphic");
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000618 Label slow;
619 Label array;
620 Label fast_object;
621 Label extra;
622 Label fast_object_grow;
623 Label fast_double_grow;
624 Label fast_double;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400625 Label maybe_name_key;
626 Label miss;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000627
628 Register value = StoreDescriptor::ValueRegister();
629 Register key = StoreDescriptor::NameRegister();
630 Register receiver = StoreDescriptor::ReceiverRegister();
631 DCHECK(receiver.is(x1));
632 DCHECK(key.is(x2));
633 DCHECK(value.is(x0));
634
635 Register receiver_map = x3;
636 Register elements = x4;
637 Register elements_map = x5;
638
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400639 __ JumpIfNotSmi(key, &maybe_name_key);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000640 __ JumpIfSmi(receiver, &slow);
641 __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
642
643 // Check that the receiver does not require access checks and is not observed.
644 // The generic stub does not perform map checks or handle observed objects.
645 __ Ldrb(x10, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
646 __ TestAndBranchIfAnySet(
647 x10, (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kIsObserved), &slow);
648
649 // Check if the object is a JS array or not.
650 Register instance_type = x10;
651 __ CompareInstanceType(receiver_map, instance_type, JS_ARRAY_TYPE);
652 __ B(eq, &array);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000653 // Check that the object is some kind of JS object EXCEPT JS Value type. In
654 // the case that the object is a value-wrapper object, we enter the runtime
655 // system to make sure that indexing into string objects works as intended.
656 STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
657 __ Cmp(instance_type, JS_OBJECT_TYPE);
658 __ B(lo, &slow);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000659
660 // Object case: Check key against length in the elements array.
661 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
662 // Check array bounds. Both the key and the length of FixedArray are smis.
663 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset));
664 __ Cmp(x10, Operand::UntagSmi(key));
665 __ B(hi, &fast_object);
666
667
668 __ Bind(&slow);
669 // Slow case, handle jump to runtime.
670 // Live values:
671 // x0: value
672 // x1: key
673 // x2: receiver
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000674 PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400675 // Never returns to here.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000676
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400677 __ bind(&maybe_name_key);
678 __ Ldr(x10, FieldMemOperand(key, HeapObject::kMapOffset));
679 __ Ldrb(x10, FieldMemOperand(x10, Map::kInstanceTypeOffset));
680 __ JumpIfNotUniqueNameInstanceType(x10, &slow);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000681
682 // The handlers in the stub cache expect a vector and slot. Since we won't
683 // change the IC from any downstream misses, a dummy vector can be used.
684 Register vector = VectorStoreICDescriptor::VectorRegister();
685 Register slot = VectorStoreICDescriptor::SlotRegister();
686 DCHECK(!AreAliased(vector, slot, x5, x6, x7, x8));
687 Handle<TypeFeedbackVector> dummy_vector =
688 TypeFeedbackVector::DummyVector(masm->isolate());
689 int slot_index = dummy_vector->GetIndex(
690 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot));
691 __ LoadRoot(vector, Heap::kDummyVectorRootIndex);
692 __ Mov(slot, Operand(Smi::FromInt(slot_index)));
693
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400694 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
695 Code::ComputeHandlerFlags(Code::STORE_IC));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000696 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags,
697 receiver, key, x5, x6, x7, x8);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400698 // Cache miss.
699 __ B(&miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000700
701 __ Bind(&extra);
702 // Extra capacity case: Check if there is extra capacity to
703 // perform the store and update the length. Used for adding one
704 // element to the array by writing to array[array.length].
705
706 // Check for room in the elements backing store.
707 // Both the key and the length of FixedArray are smis.
708 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset));
709 __ Cmp(x10, Operand::UntagSmi(key));
710 __ B(ls, &slow);
711
712 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
713 __ Cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map()));
714 __ B(eq, &fast_object_grow);
715 __ Cmp(elements_map,
716 Operand(masm->isolate()->factory()->fixed_double_array_map()));
717 __ B(eq, &fast_double_grow);
718 __ B(&slow);
719
720
721 __ Bind(&array);
722 // Array case: Get the length and the elements array from the JS
723 // array. Check that the array is in fast mode (and writable); if it
724 // is the length is always a smi.
725
726 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
727
728 // Check the key against the length in the array.
729 __ Ldrsw(x10, UntagSmiFieldMemOperand(receiver, JSArray::kLengthOffset));
730 __ Cmp(x10, Operand::UntagSmi(key));
731 __ B(eq, &extra); // We can handle the case where we are appending 1 element.
732 __ B(lo, &slow);
733
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400734 KeyedStoreGenerateMegamorphicHelper(
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000735 masm, &fast_object, &fast_double, &slow, kCheckMap, kDontIncrementLength,
736 value, key, receiver, receiver_map, elements_map, elements);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400737 KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow,
738 &fast_double_grow, &slow, kDontCheckMap,
739 kIncrementLength, value, key, receiver,
740 receiver_map, elements_map, elements);
741
742 __ bind(&miss);
743 GenerateMiss(masm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000744}
745
746
747void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
748 Register receiver = StoreDescriptor::ReceiverRegister();
749 Register name = StoreDescriptor::NameRegister();
750 DCHECK(!AreAliased(receiver, name, StoreDescriptor::ValueRegister(), x3, x4,
751 x5, x6));
752
753 // Probe the stub cache.
754 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags(
755 Code::ComputeHandlerFlags(Code::STORE_IC));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000756 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags,
757 receiver, name, x3, x4, x5, x6);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000758
759 // Cache miss: Jump to runtime.
760 GenerateMiss(masm);
761}
762
763
764void StoreIC::GenerateMiss(MacroAssembler* masm) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000765 StoreIC_PushArgs(masm);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000766
767 // Tail call to the entry.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000768 __ TailCallRuntime(Runtime::kStoreIC_Miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000769}
770
771
772void StoreIC::GenerateNormal(MacroAssembler* masm) {
773 Label miss;
774 Register value = StoreDescriptor::ValueRegister();
775 Register receiver = StoreDescriptor::ReceiverRegister();
776 Register name = StoreDescriptor::NameRegister();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000777 Register dictionary = x5;
778 DCHECK(!AreAliased(value, receiver, name,
779 VectorStoreICDescriptor::SlotRegister(),
780 VectorStoreICDescriptor::VectorRegister(), x5, x6, x7));
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000781
782 __ Ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
783
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000784 GenerateDictionaryStore(masm, &miss, dictionary, name, value, x6, x7);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000785 Counters* counters = masm->isolate()->counters();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000786 __ IncrementCounter(counters->store_normal_hit(), 1, x6, x7);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000787 __ Ret();
788
789 // Cache miss: Jump to runtime.
790 __ Bind(&miss);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000791 __ IncrementCounter(counters->store_normal_miss(), 1, x6, x7);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000792 GenerateMiss(masm);
793}
794
795
796Condition CompareIC::ComputeCondition(Token::Value op) {
797 switch (op) {
798 case Token::EQ_STRICT:
799 case Token::EQ:
800 return eq;
801 case Token::LT:
802 return lt;
803 case Token::GT:
804 return gt;
805 case Token::LTE:
806 return le;
807 case Token::GTE:
808 return ge;
809 default:
810 UNREACHABLE();
811 return al;
812 }
813}
814
815
816bool CompareIC::HasInlinedSmiCode(Address address) {
817 // The address of the instruction following the call.
818 Address info_address = Assembler::return_address_from_call_start(address);
819
820 InstructionSequence* patch_info = InstructionSequence::At(info_address);
821 return patch_info->IsInlineData();
822}
823
824
825// Activate a SMI fast-path by patching the instructions generated by
826// JumpPatchSite::EmitJumpIf(Not)Smi(), using the information encoded by
827// JumpPatchSite::EmitPatchInfo().
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000828void PatchInlinedSmiCode(Isolate* isolate, Address address,
829 InlinedSmiCheck check) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000830 // The patch information is encoded in the instruction stream using
831 // instructions which have no side effects, so we can safely execute them.
832 // The patch information is encoded directly after the call to the helper
833 // function which is requesting this patch operation.
834 Address info_address = Assembler::return_address_from_call_start(address);
835 InlineSmiCheckInfo info(info_address);
836
837 // Check and decode the patch information instruction.
838 if (!info.HasSmiCheck()) {
839 return;
840 }
841
842 if (FLAG_trace_ic) {
843 PrintF("[ Patching ic at %p, marker=%p, SMI check=%p\n", address,
844 info_address, reinterpret_cast<void*>(info.SmiCheck()));
845 }
846
847 // Patch and activate code generated by JumpPatchSite::EmitJumpIfNotSmi()
848 // and JumpPatchSite::EmitJumpIfSmi().
849 // Changing
850 // tb(n)z xzr, #0, <target>
851 // to
852 // tb(!n)z test_reg, #0, <target>
853 Instruction* to_patch = info.SmiCheck();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000854 PatchingAssembler patcher(isolate, to_patch, 1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000855 DCHECK(to_patch->IsTestBranch());
856 DCHECK(to_patch->ImmTestBranchBit5() == 0);
857 DCHECK(to_patch->ImmTestBranchBit40() == 0);
858
859 STATIC_ASSERT(kSmiTag == 0);
860 STATIC_ASSERT(kSmiTagMask == 1);
861
862 int branch_imm = to_patch->ImmTestBranch();
863 Register smi_reg;
864 if (check == ENABLE_INLINED_SMI_CHECK) {
865 DCHECK(to_patch->Rt() == xzr.code());
866 smi_reg = info.SmiRegister();
867 } else {
868 DCHECK(check == DISABLE_INLINED_SMI_CHECK);
869 DCHECK(to_patch->Rt() != xzr.code());
870 smi_reg = xzr;
871 }
872
873 if (to_patch->Mask(TestBranchMask) == TBZ) {
874 // This is JumpIfNotSmi(smi_reg, branch_imm).
875 patcher.tbnz(smi_reg, 0, branch_imm);
876 } else {
877 DCHECK(to_patch->Mask(TestBranchMask) == TBNZ);
878 // This is JumpIfSmi(smi_reg, branch_imm).
879 patcher.tbz(smi_reg, 0, branch_imm);
880 }
881}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000882} // namespace internal
883} // namespace v8
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000884
885#endif // V8_TARGET_ARCH_ARM64