• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if V8_TARGET_ARCH_MIPS
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 
12 namespace v8 {
13 namespace internal {
14 
15 
16 // ----------------------------------------------------------------------------
17 // Static IC stub generators.
18 //
19 
20 #define __ ACCESS_MASM(masm)
21 
22 
GenerateGlobalInstanceTypeCheck(MacroAssembler * masm,Register type,Label * global_object)23 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type,
24                                             Label* global_object) {
25   // Register usage:
26   //   type: holds the receiver instance type on entry.
27   __ Branch(global_object, eq, type, Operand(JS_GLOBAL_OBJECT_TYPE));
28   __ Branch(global_object, eq, type, Operand(JS_GLOBAL_PROXY_TYPE));
29 }
30 
31 
32 // Helper function used from LoadIC GenerateNormal.
33 //
34 // elements: Property dictionary. It is not clobbered if a jump to the miss
35 //           label is done.
36 // name:     Property name. It is not clobbered if a jump to the miss label is
37 //           done
38 // result:   Register for the result. It is only updated if a jump to the miss
39 //           label is not done. Can be the same as elements or name clobbering
40 //           one of these in the case of not jumping to the miss label.
41 // The two scratch registers need to be different from elements, name and
42 // result.
43 // The generated code assumes that the receiver has slow properties,
44 // is not a global object and does not have interceptors.
45 // The address returned from GenerateStringDictionaryProbes() in scratch2
46 // is used.
GenerateDictionaryLoad(MacroAssembler * masm,Label * miss,Register elements,Register name,Register result,Register scratch1,Register scratch2)47 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
48                                    Register elements, Register name,
49                                    Register result, Register scratch1,
50                                    Register scratch2) {
51   // Main use of the scratch registers.
52   // scratch1: Used as temporary and to hold the capacity of the property
53   //           dictionary.
54   // scratch2: Used as temporary.
55   Label done;
56 
57   // Probe the dictionary.
58   NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
59                                                    name, scratch1, scratch2);
60 
61   // If probing finds an entry check that the value is a normal
62   // property.
63   __ bind(&done);  // scratch2 == elements + 4 * index.
64   const int kElementsStartOffset =
65       NameDictionary::kHeaderSize +
66       NameDictionary::kElementsStartIndex * kPointerSize;
67   const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
68   __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
69   __ And(at, scratch1,
70          Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
71   __ Branch(miss, ne, at, Operand(zero_reg));
72 
73   // Get the value at the masked, scaled index and return.
74   __ lw(result,
75         FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
76 }
77 
78 
79 // Helper function used from StoreIC::GenerateNormal.
80 //
81 // elements: Property dictionary. It is not clobbered if a jump to the miss
82 //           label is done.
83 // name:     Property name. It is not clobbered if a jump to the miss label is
84 //           done
85 // value:    The value to store.
86 // The two scratch registers need to be different from elements, name and
87 // result.
88 // The generated code assumes that the receiver has slow properties,
89 // is not a global object and does not have interceptors.
90 // The address returned from GenerateStringDictionaryProbes() in scratch2
91 // is used.
GenerateDictionaryStore(MacroAssembler * masm,Label * miss,Register elements,Register name,Register value,Register scratch1,Register scratch2)92 static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
93                                     Register elements, Register name,
94                                     Register value, Register scratch1,
95                                     Register scratch2) {
96   // Main use of the scratch registers.
97   // scratch1: Used as temporary and to hold the capacity of the property
98   //           dictionary.
99   // scratch2: Used as temporary.
100   Label done;
101 
102   // Probe the dictionary.
103   NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
104                                                    name, scratch1, scratch2);
105 
106   // If probing finds an entry in the dictionary check that the value
107   // is a normal property that is not read only.
108   __ bind(&done);  // scratch2 == elements + 4 * index.
109   const int kElementsStartOffset =
110       NameDictionary::kHeaderSize +
111       NameDictionary::kElementsStartIndex * kPointerSize;
112   const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
113   const int kTypeAndReadOnlyMask =
114       (PropertyDetails::TypeField::kMask |
115        PropertyDetails::AttributesField::encode(READ_ONLY))
116       << kSmiTagSize;
117   __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
118   __ And(at, scratch1, Operand(kTypeAndReadOnlyMask));
119   __ Branch(miss, ne, at, Operand(zero_reg));
120 
121   // Store the value at the masked, scaled index and return.
122   const int kValueOffset = kElementsStartOffset + kPointerSize;
123   __ Addu(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
124   __ sw(value, MemOperand(scratch2));
125 
126   // Update the write barrier. Make sure not to clobber the value.
127   __ mov(scratch1, value);
128   __ RecordWrite(elements, scratch2, scratch1, kRAHasNotBeenSaved,
129                  kDontSaveFPRegs);
130 }
131 
132 
133 // Checks the receiver for special cases (value type, slow case bits).
134 // Falls through for regular JS object.
GenerateKeyedLoadReceiverCheck(MacroAssembler * masm,Register receiver,Register map,Register scratch,int interceptor_bit,Label * slow)135 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
136                                            Register receiver, Register map,
137                                            Register scratch,
138                                            int interceptor_bit, Label* slow) {
139   // Check that the object isn't a smi.
140   __ JumpIfSmi(receiver, slow);
141   // Get the map of the receiver.
142   __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
143   // Check bit field.
144   __ lbu(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
145   __ And(at, scratch,
146          Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
147   __ Branch(slow, ne, at, Operand(zero_reg));
148   // Check that the object is some kind of JS object EXCEPT JS Value type.
149   // In the case that the object is a value-wrapper object,
150   // we enter the runtime system to make sure that indexing into string
151   // objects work as intended.
152   DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE);
153   __ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
154   __ Branch(slow, lt, scratch, Operand(JS_OBJECT_TYPE));
155 }
156 
157 
158 // Loads an indexed element from a fast case array.
GenerateFastArrayLoad(MacroAssembler * masm,Register receiver,Register key,Register elements,Register scratch1,Register scratch2,Register result,Label * slow)159 static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver,
160                                   Register key, Register elements,
161                                   Register scratch1, Register scratch2,
162                                   Register result, Label* slow) {
163   // Register use:
164   //
165   // receiver - holds the receiver on entry.
166   //            Unchanged unless 'result' is the same register.
167   //
168   // key      - holds the smi key on entry.
169   //            Unchanged unless 'result' is the same register.
170   //
171   // result   - holds the result on exit if the load succeeded.
172   //            Allowed to be the the same as 'receiver' or 'key'.
173   //            Unchanged on bailout so 'receiver' and 'key' can be safely
174   //            used by further computation.
175   //
176   // Scratch registers:
177   //
178   // elements - holds the elements of the receiver and its prototypes.
179   //
180   // scratch1 - used to hold elements length, bit fields, base addresses.
181   //
182   // scratch2 - used to hold maps, prototypes, and the loaded value.
183   Label check_prototypes, check_next_prototype;
184   Label done, in_bounds, absent;
185 
186   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
187   __ AssertFastElements(elements);
188 
189   // Check that the key (index) is within bounds.
190   __ lw(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
191   __ Branch(&in_bounds, lo, key, Operand(scratch1));
192   // Out-of-bounds. Check the prototype chain to see if we can just return
193   // 'undefined'.
194   // Negative keys can't take the fast OOB path.
195   __ Branch(slow, lt, key, Operand(zero_reg));
196   __ bind(&check_prototypes);
197   __ lw(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
198   __ bind(&check_next_prototype);
199   __ lw(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset));
200   // scratch2: current prototype
201   __ LoadRoot(at, Heap::kNullValueRootIndex);
202   __ Branch(&absent, eq, scratch2, Operand(at));
203   __ lw(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset));
204   __ lw(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset));
205   // elements: elements of current prototype
206   // scratch2: map of current prototype
207   __ lbu(scratch1, FieldMemOperand(scratch2, Map::kInstanceTypeOffset));
208   __ Branch(slow, lo, scratch1, Operand(JS_OBJECT_TYPE));
209   __ lbu(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset));
210   __ And(at, scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
211                                (1 << Map::kHasIndexedInterceptor)));
212   __ Branch(slow, ne, at, Operand(zero_reg));
213   __ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex);
214   __ Branch(slow, ne, elements, Operand(at));
215   __ Branch(&check_next_prototype);
216 
217   __ bind(&absent);
218   __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
219   __ Branch(&done);
220 
221   __ bind(&in_bounds);
222   // Fast case: Do the load.
223   __ Addu(scratch1, elements,
224           Operand(FixedArray::kHeaderSize - kHeapObjectTag));
225   // The key is a smi.
226   STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
227   __ Lsa(at, scratch1, key, kPointerSizeLog2 - kSmiTagSize);
228   __ lw(scratch2, MemOperand(at));
229 
230   __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
231   // In case the loaded value is the_hole we have to check the prototype chain.
232   __ Branch(&check_prototypes, eq, scratch2, Operand(at));
233   __ Move(result, scratch2);
234   __ bind(&done);
235 }
236 
237 
238 // Checks whether a key is an array index string or a unique name.
239 // Falls through if a key is a unique name.
GenerateKeyNameCheck(MacroAssembler * masm,Register key,Register map,Register hash,Label * index_string,Label * not_unique)240 static void GenerateKeyNameCheck(MacroAssembler* masm, Register key,
241                                  Register map, Register hash,
242                                  Label* index_string, Label* not_unique) {
243   // The key is not a smi.
244   Label unique;
245   // Is it a name?
246   __ GetObjectType(key, map, hash);
247   __ Branch(not_unique, hi, hash, Operand(LAST_UNIQUE_NAME_TYPE));
248   STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
249   __ Branch(&unique, eq, hash, Operand(LAST_UNIQUE_NAME_TYPE));
250 
251   // Is the string an array index, with cached numeric value?
252   __ lw(hash, FieldMemOperand(key, Name::kHashFieldOffset));
253   __ And(at, hash, Operand(Name::kContainsCachedArrayIndexMask));
254   __ Branch(index_string, eq, at, Operand(zero_reg));
255 
256   // Is the string internalized? We know it's a string, so a single
257   // bit test is enough.
258   // map: key map
259   __ lbu(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
260   STATIC_ASSERT(kInternalizedTag == 0);
261   __ And(at, hash, Operand(kIsNotInternalizedMask));
262   __ Branch(not_unique, ne, at, Operand(zero_reg));
263 
264   __ bind(&unique);
265 }
266 
GenerateNormal(MacroAssembler * masm)267 void LoadIC::GenerateNormal(MacroAssembler* masm) {
268   Register dictionary = a0;
269   DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
270   DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
271 
272   Label slow;
273 
274   __ lw(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
275                                     JSObject::kPropertiesOffset));
276   GenerateDictionaryLoad(masm, &slow, dictionary,
277                          LoadDescriptor::NameRegister(), v0, a3, t0);
278   __ Ret();
279 
280   // Dictionary load failed, go slow (but don't miss).
281   __ bind(&slow);
282   GenerateRuntimeGetProperty(masm);
283 }
284 
285 
286 // A register that isn't one of the parameters to the load ic.
LoadIC_TempRegister()287 static const Register LoadIC_TempRegister() { return a3; }
288 
289 
LoadIC_PushArgs(MacroAssembler * masm)290 static void LoadIC_PushArgs(MacroAssembler* masm) {
291   Register receiver = LoadDescriptor::ReceiverRegister();
292   Register name = LoadDescriptor::NameRegister();
293   Register slot = LoadDescriptor::SlotRegister();
294   Register vector = LoadWithVectorDescriptor::VectorRegister();
295 
296   __ Push(receiver, name, slot, vector);
297 }
298 
299 
GenerateMiss(MacroAssembler * masm)300 void LoadIC::GenerateMiss(MacroAssembler* masm) {
301   // The return address is in ra.
302   Isolate* isolate = masm->isolate();
303 
304   DCHECK(!AreAliased(t0, t1, LoadWithVectorDescriptor::SlotRegister(),
305                      LoadWithVectorDescriptor::VectorRegister()));
306   __ IncrementCounter(isolate->counters()->ic_load_miss(), 1, t0, t1);
307 
308   LoadIC_PushArgs(masm);
309 
310   // Perform tail call to the entry.
311   __ TailCallRuntime(Runtime::kLoadIC_Miss);
312 }
313 
GenerateRuntimeGetProperty(MacroAssembler * masm)314 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
315   // The return address is in ra.
316 
317   __ mov(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
318   __ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
319 
320   // Do tail-call to runtime routine.
321   __ TailCallRuntime(Runtime::kGetProperty);
322 }
323 
324 
GenerateMiss(MacroAssembler * masm)325 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
326   // The return address is in ra.
327   Isolate* isolate = masm->isolate();
328 
329   DCHECK(!AreAliased(t0, t1, LoadWithVectorDescriptor::SlotRegister(),
330                      LoadWithVectorDescriptor::VectorRegister()));
331   __ IncrementCounter(isolate->counters()->ic_keyed_load_miss(), 1, t0, t1);
332 
333   LoadIC_PushArgs(masm);
334 
335   // Perform tail call to the entry.
336   __ TailCallRuntime(Runtime::kKeyedLoadIC_Miss);
337 }
338 
GenerateRuntimeGetProperty(MacroAssembler * masm)339 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
340   // The return address is in ra.
341 
342   __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
343 
344   // Do tail-call to runtime routine.
345   __ TailCallRuntime(Runtime::kKeyedGetProperty);
346 }
347 
GenerateMegamorphic(MacroAssembler * masm)348 void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm) {
349   // The return address is in ra.
350   Label slow, check_name, index_smi, index_name, property_array_property;
351   Label probe_dictionary, check_number_dictionary;
352 
353   Register key = LoadDescriptor::NameRegister();
354   Register receiver = LoadDescriptor::ReceiverRegister();
355   DCHECK(key.is(a2));
356   DCHECK(receiver.is(a1));
357 
358   Isolate* isolate = masm->isolate();
359 
360   // Check that the key is a smi.
361   __ JumpIfNotSmi(key, &check_name);
362   __ bind(&index_smi);
363   // Now the key is known to be a smi. This place is also jumped to from below
364   // where a numeric string is converted to a smi.
365 
366   GenerateKeyedLoadReceiverCheck(masm, receiver, a0, a3,
367                                  Map::kHasIndexedInterceptor, &slow);
368 
369   // Check the receiver's map to see if it has fast elements.
370   __ CheckFastElements(a0, a3, &check_number_dictionary);
371 
372   GenerateFastArrayLoad(masm, receiver, key, a0, a3, t0, v0, &slow);
373   __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_smi(), 1, t0,
374                       a3);
375   __ Ret();
376 
377   __ bind(&check_number_dictionary);
378   __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset));
379   __ lw(a3, FieldMemOperand(t0, JSObject::kMapOffset));
380 
381   // Check whether the elements is a number dictionary.
382   // a3: elements map
383   // t0: elements
384   __ LoadRoot(at, Heap::kHashTableMapRootIndex);
385   __ Branch(&slow, ne, a3, Operand(at));
386   __ sra(a0, key, kSmiTagSize);
387   __ LoadFromNumberDictionary(&slow, t0, key, v0, a0, a3, t1);
388   __ Ret();
389 
390   // Slow case, key and receiver still in a2 and a1.
391   __ bind(&slow);
392   __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_slow(), 1, t0,
393                       a3);
394   GenerateRuntimeGetProperty(masm);
395 
396   __ bind(&check_name);
397   GenerateKeyNameCheck(masm, key, a0, a3, &index_name, &slow);
398 
399   GenerateKeyedLoadReceiverCheck(masm, receiver, a0, a3,
400                                  Map::kHasNamedInterceptor, &slow);
401 
402 
403   // If the receiver is a fast-case object, check the stub cache. Otherwise
404   // probe the dictionary.
405   __ lw(a3, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
406   __ lw(t0, FieldMemOperand(a3, HeapObject::kMapOffset));
407   __ LoadRoot(at, Heap::kHashTableMapRootIndex);
408   __ Branch(&probe_dictionary, eq, t0, Operand(at));
409 
410   // The handlers in the stub cache expect a vector and slot. Since we won't
411   // change the IC from any downstream misses, a dummy vector can be used.
412   Register vector = LoadWithVectorDescriptor::VectorRegister();
413   Register slot = LoadWithVectorDescriptor::SlotRegister();
414   DCHECK(!AreAliased(vector, slot, t0, t1, t2, t5));
415   Handle<TypeFeedbackVector> dummy_vector =
416       TypeFeedbackVector::DummyVector(masm->isolate());
417   int slot_index = dummy_vector->GetIndex(
418       FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot));
419   __ LoadRoot(vector, Heap::kDummyVectorRootIndex);
420   __ li(slot, Operand(Smi::FromInt(slot_index)));
421 
422   Code::Flags flags =
423       Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::LOAD_IC));
424   masm->isolate()->stub_cache()->GenerateProbe(masm, Code::KEYED_LOAD_IC, flags,
425                                                receiver, key, t0, t1, t2, t5);
426   // Cache miss.
427   GenerateMiss(masm);
428 
429   // Do a quick inline probe of the receiver's dictionary, if it
430   // exists.
431   __ bind(&probe_dictionary);
432   // a3: elements
433   __ lw(a0, FieldMemOperand(receiver, HeapObject::kMapOffset));
434   __ lbu(a0, FieldMemOperand(a0, Map::kInstanceTypeOffset));
435   GenerateGlobalInstanceTypeCheck(masm, a0, &slow);
436   // Load the property to v0.
437   GenerateDictionaryLoad(masm, &slow, a3, key, v0, t1, t0);
438   __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_symbol(), 1,
439                       t0, a3);
440   __ Ret();
441 
442   __ bind(&index_name);
443   __ IndexFromHash(a3, key);
444   // Now jump to the place where smi keys are handled.
445   __ Branch(&index_smi);
446 }
447 
448 
KeyedStoreGenerateMegamorphicHelper(MacroAssembler * masm,Label * fast_object,Label * fast_double,Label * slow,KeyedStoreCheckMap check_map,KeyedStoreIncrementLength increment_length,Register value,Register key,Register receiver,Register receiver_map,Register elements_map,Register elements)449 static void KeyedStoreGenerateMegamorphicHelper(
450     MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
451     KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length,
452     Register value, Register key, Register receiver, Register receiver_map,
453     Register elements_map, Register elements) {
454   Label transition_smi_elements;
455   Label finish_object_store, non_double_value, transition_double_elements;
456   Label fast_double_without_map_check;
457 
458   // Fast case: Do the store, could be either Object or double.
459   __ bind(fast_object);
460   Register scratch = t0;
461   Register scratch2 = t4;
462   Register scratch3 = t5;
463   Register address = t1;
464   DCHECK(!AreAliased(value, key, receiver, receiver_map, elements_map, elements,
465                      scratch, scratch2, scratch3, address));
466 
467   if (check_map == kCheckMap) {
468     __ lw(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
469     __ Branch(fast_double, ne, elements_map,
470               Operand(masm->isolate()->factory()->fixed_array_map()));
471   }
472 
473   // HOLECHECK: guards "A[i] = V"
474   // We have to go to the runtime if the current value is the hole because
475   // there may be a callback on the element.
476   Label holecheck_passed1;
477   __ Addu(address, elements, FixedArray::kHeaderSize - kHeapObjectTag);
478   __ Lsa(address, address, key, kPointerSizeLog2 - kSmiTagSize);
479   __ lw(scratch, MemOperand(address));
480   __ Branch(&holecheck_passed1, ne, scratch,
481             Operand(masm->isolate()->factory()->the_hole_value()));
482   __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
483 
484   __ bind(&holecheck_passed1);
485 
486   // Smi stores don't require further checks.
487   Label non_smi_value;
488   __ JumpIfNotSmi(value, &non_smi_value);
489 
490   if (increment_length == kIncrementLength) {
491     // Add 1 to receiver->length.
492     __ Addu(scratch, key, Operand(Smi::FromInt(1)));
493     __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
494   }
495   // It's irrelevant whether array is smi-only or not when writing a smi.
496   __ Addu(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
497   __ Lsa(address, address, key, kPointerSizeLog2 - kSmiTagSize);
498   __ sw(value, MemOperand(address));
499   __ Ret();
500 
501   __ bind(&non_smi_value);
502   // Escape to elements kind transition case.
503   __ CheckFastObjectElements(receiver_map, scratch, &transition_smi_elements);
504 
505   // Fast elements array, store the value to the elements backing store.
506   __ bind(&finish_object_store);
507   if (increment_length == kIncrementLength) {
508     // Add 1 to receiver->length.
509     __ Addu(scratch, key, Operand(Smi::FromInt(1)));
510     __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
511   }
512   __ Addu(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
513   __ Lsa(address, address, key, kPointerSizeLog2 - kSmiTagSize);
514   __ sw(value, MemOperand(address));
515   // Update write barrier for the elements array address.
516   __ mov(scratch, value);  // Preserve the value which is returned.
517   __ RecordWrite(elements, address, scratch, kRAHasNotBeenSaved,
518                  kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
519   __ Ret();
520 
521   __ bind(fast_double);
522   if (check_map == kCheckMap) {
523     // Check for fast double array case. If this fails, call through to the
524     // runtime.
525     __ LoadRoot(at, Heap::kFixedDoubleArrayMapRootIndex);
526     __ Branch(slow, ne, elements_map, Operand(at));
527   }
528 
529   // HOLECHECK: guards "A[i] double hole?"
530   // We have to see if the double version of the hole is present. If so
531   // go to the runtime.
532   __ Addu(address, elements, Operand(FixedDoubleArray::kHeaderSize +
533                                      kHoleNanUpper32Offset - kHeapObjectTag));
534   __ Lsa(address, address, key, kPointerSizeLog2);
535   __ lw(scratch, MemOperand(address));
536   __ Branch(&fast_double_without_map_check, ne, scratch,
537             Operand(kHoleNanUpper32));
538   __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
539 
540   __ bind(&fast_double_without_map_check);
541   __ StoreNumberToDoubleElements(value, key, elements, scratch, scratch2,
542                                  scratch3, &transition_double_elements);
543   if (increment_length == kIncrementLength) {
544     // Add 1 to receiver->length.
545     __ Addu(scratch, key, Operand(Smi::FromInt(1)));
546     __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
547   }
548   __ Ret();
549 
550   __ bind(&transition_smi_elements);
551   // Transition the array appropriately depending on the value type.
552   __ lw(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
553   __ LoadRoot(at, Heap::kHeapNumberMapRootIndex);
554   __ Branch(&non_double_value, ne, scratch, Operand(at));
555 
556   // Value is a double. Transition FAST_SMI_ELEMENTS ->
557   // FAST_DOUBLE_ELEMENTS and complete the store.
558   __ LoadTransitionedArrayMapConditional(
559       FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, scratch, slow);
560   AllocationSiteMode mode =
561       AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS);
562   ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value,
563                                                    receiver_map, mode, slow);
564   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
565   __ jmp(&fast_double_without_map_check);
566 
567   __ bind(&non_double_value);
568   // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
569   __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS,
570                                          receiver_map, scratch, slow);
571   mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
572   ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
573       masm, receiver, key, value, receiver_map, mode, slow);
574   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
575   __ jmp(&finish_object_store);
576 
577   __ bind(&transition_double_elements);
578   // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
579   // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
580   // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
581   __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS,
582                                          receiver_map, scratch, slow);
583   mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
584   ElementsTransitionGenerator::GenerateDoubleToObject(
585       masm, receiver, key, value, receiver_map, mode, slow);
586   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
587   __ jmp(&finish_object_store);
588 }
589 
590 
GenerateMegamorphic(MacroAssembler * masm,LanguageMode language_mode)591 void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm,
592                                        LanguageMode language_mode) {
593   // ---------- S t a t e --------------
594   //  -- a0     : value
595   //  -- a1     : key
596   //  -- a2     : receiver
597   //  -- ra     : return address
598   // -----------------------------------
599   Label slow, fast_object, fast_object_grow;
600   Label fast_double, fast_double_grow;
601   Label array, extra, check_if_double_array, maybe_name_key, miss;
602 
603   // Register usage.
604   Register value = StoreDescriptor::ValueRegister();
605   Register key = StoreDescriptor::NameRegister();
606   Register receiver = StoreDescriptor::ReceiverRegister();
607   DCHECK(value.is(a0));
608   Register receiver_map = a3;
609   Register elements_map = t2;
610   Register elements = t3;  // Elements array of the receiver.
611   // t0 and t1 are used as general scratch registers.
612 
613   // Check that the key is a smi.
614   __ JumpIfNotSmi(key, &maybe_name_key);
615   // Check that the object isn't a smi.
616   __ JumpIfSmi(receiver, &slow);
617   // Get the map of the object.
618   __ lw(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
619   // Check that the receiver does not require access checks.
620   // The generic stub does not perform map checks.
621   __ lbu(t0, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
622   __ And(t0, t0, Operand(1 << Map::kIsAccessCheckNeeded));
623   __ Branch(&slow, ne, t0, Operand(zero_reg));
624   // Check if the object is a JS array or not.
625   __ lbu(t0, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset));
626   __ Branch(&array, eq, t0, Operand(JS_ARRAY_TYPE));
627   // Check that the object is some kind of JS object EXCEPT JS Value type. In
628   // the case that the object is a value-wrapper object, we enter the runtime
629   // system to make sure that indexing into string objects works as intended.
630   STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
631   __ Branch(&slow, lo, t0, Operand(JS_OBJECT_TYPE));
632 
633   // Object case: Check key against length in the elements array.
634   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
635   // Check array bounds. Both the key and the length of FixedArray are smis.
636   __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
637   __ Branch(&fast_object, lo, key, Operand(t0));
638 
639   // Slow case, handle jump to runtime.
640   __ bind(&slow);
641   // Entry registers are intact.
642   // a0: value.
643   // a1: key.
644   // a2: receiver.
645   PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode);
646   // Never returns to here.
647 
648   __ bind(&maybe_name_key);
649   __ lw(t0, FieldMemOperand(key, HeapObject::kMapOffset));
650   __ lb(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
651   __ JumpIfNotUniqueNameInstanceType(t0, &slow);
652 
653   // The handlers in the stub cache expect a vector and slot. Since we won't
654   // change the IC from any downstream misses, a dummy vector can be used.
655   Register vector = VectorStoreICDescriptor::VectorRegister();
656   Register slot = VectorStoreICDescriptor::SlotRegister();
657   DCHECK(!AreAliased(vector, slot, t1, t2, t4, t5));
658   Handle<TypeFeedbackVector> dummy_vector =
659       TypeFeedbackVector::DummyVector(masm->isolate());
660   int slot_index = dummy_vector->GetIndex(
661       FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot));
662   __ LoadRoot(vector, Heap::kDummyVectorRootIndex);
663   __ li(slot, Operand(Smi::FromInt(slot_index)));
664 
665   Code::Flags flags =
666       Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::STORE_IC));
667   masm->isolate()->stub_cache()->GenerateProbe(
668       masm, Code::KEYED_STORE_IC, flags, receiver, key, t1, t2, t4, t5);
669   // Cache miss.
670   __ Branch(&miss);
671 
672   // Extra capacity case: Check if there is extra capacity to
673   // perform the store and update the length. Used for adding one
674   // element to the array by writing to array[array.length].
675   __ bind(&extra);
676   // Condition code from comparing key and array length is still available.
677   // Only support writing to array[array.length].
678   __ Branch(&slow, ne, key, Operand(t0));
679   // Check for room in the elements backing store.
680   // Both the key and the length of FixedArray are smis.
681   __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
682   __ Branch(&slow, hs, key, Operand(t0));
683   __ lw(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
684   __ Branch(&check_if_double_array, ne, elements_map,
685             Heap::kFixedArrayMapRootIndex);
686 
687   __ jmp(&fast_object_grow);
688 
689   __ bind(&check_if_double_array);
690   __ Branch(&slow, ne, elements_map, Heap::kFixedDoubleArrayMapRootIndex);
691   __ jmp(&fast_double_grow);
692 
693   // Array case: Get the length and the elements array from the JS
694   // array. Check that the array is in fast mode (and writable); if it
695   // is the length is always a smi.
696   __ bind(&array);
697   __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
698 
699   // Check the key against the length in the array.
700   __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
701   __ Branch(&extra, hs, key, Operand(t0));
702 
703   KeyedStoreGenerateMegamorphicHelper(
704       masm, &fast_object, &fast_double, &slow, kCheckMap, kDontIncrementLength,
705       value, key, receiver, receiver_map, elements_map, elements);
706   KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow,
707                                       &fast_double_grow, &slow, kDontCheckMap,
708                                       kIncrementLength, value, key, receiver,
709                                       receiver_map, elements_map, elements);
710 
711   __ bind(&miss);
712   GenerateMiss(masm);
713 }
714 
715 
StoreIC_PushArgs(MacroAssembler * masm)716 static void StoreIC_PushArgs(MacroAssembler* masm) {
717   __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
718           StoreDescriptor::ValueRegister(),
719           VectorStoreICDescriptor::SlotRegister(),
720           VectorStoreICDescriptor::VectorRegister());
721 }
722 
723 
GenerateMiss(MacroAssembler * masm)724 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
725   StoreIC_PushArgs(masm);
726 
727   __ TailCallRuntime(Runtime::kKeyedStoreIC_Miss);
728 }
729 
GenerateMiss(MacroAssembler * masm)730 void StoreIC::GenerateMiss(MacroAssembler* masm) {
731   StoreIC_PushArgs(masm);
732 
733   // Perform tail call to the entry.
734   __ TailCallRuntime(Runtime::kStoreIC_Miss);
735 }
736 
737 
GenerateNormal(MacroAssembler * masm)738 void StoreIC::GenerateNormal(MacroAssembler* masm) {
739   Label miss;
740   Register receiver = StoreDescriptor::ReceiverRegister();
741   Register name = StoreDescriptor::NameRegister();
742   Register value = StoreDescriptor::ValueRegister();
743   Register dictionary = t1;
744   DCHECK(receiver.is(a1));
745   DCHECK(name.is(a2));
746   DCHECK(value.is(a0));
747   DCHECK(VectorStoreICDescriptor::VectorRegister().is(a3));
748   DCHECK(VectorStoreICDescriptor::SlotRegister().is(t0));
749 
750   __ lw(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
751 
752   GenerateDictionaryStore(masm, &miss, dictionary, name, value, t2, t5);
753   Counters* counters = masm->isolate()->counters();
754   __ IncrementCounter(counters->ic_store_normal_hit(), 1, t2, t5);
755   __ Ret();
756 
757   __ bind(&miss);
758   __ IncrementCounter(counters->ic_store_normal_miss(), 1, t2, t5);
759   GenerateMiss(masm);
760 }
761 
762 
763 #undef __
764 
765 
ComputeCondition(Token::Value op)766 Condition CompareIC::ComputeCondition(Token::Value op) {
767   switch (op) {
768     case Token::EQ_STRICT:
769     case Token::EQ:
770       return eq;
771     case Token::LT:
772       return lt;
773     case Token::GT:
774       return gt;
775     case Token::LTE:
776       return le;
777     case Token::GTE:
778       return ge;
779     default:
780       UNREACHABLE();
781       return kNoCondition;
782   }
783 }
784 
785 
HasInlinedSmiCode(Address address)786 bool CompareIC::HasInlinedSmiCode(Address address) {
787   // The address of the instruction following the call.
788   Address andi_instruction_address =
789       address + Assembler::kCallTargetAddressOffset;
790 
791   // If the instruction following the call is not a andi at, rx, #yyy, nothing
792   // was inlined.
793   Instr instr = Assembler::instr_at(andi_instruction_address);
794   return Assembler::IsAndImmediate(instr) &&
795          Assembler::GetRt(instr) == static_cast<uint32_t>(zero_reg.code());
796 }
797 
798 
PatchInlinedSmiCode(Isolate * isolate,Address address,InlinedSmiCheck check)799 void PatchInlinedSmiCode(Isolate* isolate, Address address,
800                          InlinedSmiCheck check) {
801   Address andi_instruction_address =
802       address + Assembler::kCallTargetAddressOffset;
803 
804   // If the instruction following the call is not a andi at, rx, #yyy, nothing
805   // was inlined.
806   Instr instr = Assembler::instr_at(andi_instruction_address);
807   if (!(Assembler::IsAndImmediate(instr) &&
808         Assembler::GetRt(instr) == static_cast<uint32_t>(zero_reg.code()))) {
809     return;
810   }
811 
812   // The delta to the start of the map check instruction and the
813   // condition code uses at the patched jump.
814   int delta = Assembler::GetImmediate16(instr);
815   delta += Assembler::GetRs(instr) * kImm16Mask;
816   // If the delta is 0 the instruction is andi at, zero_reg, #0 which also
817   // signals that nothing was inlined.
818   if (delta == 0) {
819     return;
820   }
821 
822   if (FLAG_trace_ic) {
823     PrintF("[  patching ic at %p, andi=%p, delta=%d\n",
824            static_cast<void*>(address),
825            static_cast<void*>(andi_instruction_address), delta);
826   }
827 
828   Address patch_address =
829       andi_instruction_address - delta * Instruction::kInstrSize;
830   Instr instr_at_patch = Assembler::instr_at(patch_address);
831   // This is patching a conditional "jump if not smi/jump if smi" site.
832   // Enabling by changing from
833   //   andi at, rx, 0
834   //   Branch <target>, eq, at, Operand(zero_reg)
835   // to:
836   //   andi at, rx, #kSmiTagMask
837   //   Branch <target>, ne, at, Operand(zero_reg)
838   // and vice-versa to be disabled again.
839   CodePatcher patcher(isolate, patch_address, 2);
840   Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
841   if (check == ENABLE_INLINED_SMI_CHECK) {
842     DCHECK(Assembler::IsAndImmediate(instr_at_patch));
843     DCHECK_EQ(0u, Assembler::GetImmediate16(instr_at_patch));
844     patcher.masm()->andi(at, reg, kSmiTagMask);
845   } else {
846     DCHECK_EQ(check, DISABLE_INLINED_SMI_CHECK);
847     DCHECK(Assembler::IsAndImmediate(instr_at_patch));
848     patcher.masm()->andi(at, reg, 0);
849   }
850   Instr branch_instr =
851       Assembler::instr_at(patch_address + Instruction::kInstrSize);
852   DCHECK(Assembler::IsBranch(branch_instr));
853 
854   uint32_t opcode = Assembler::GetOpcodeField(branch_instr);
855   // Currently only the 'eq' and 'ne' cond values are supported and the simple
856   // branch instructions and their r6 variants (with opcode being the branch
857   // type). There are some special cases (see Assembler::IsBranch()) so
858   // extending this would be tricky.
859   DCHECK(opcode == BEQ ||    // BEQ
860          opcode == BNE ||    // BNE
861          opcode == POP10 ||  // BEQC
862          opcode == POP30 ||  // BNEC
863          opcode == POP66 ||  // BEQZC
864          opcode == POP76);   // BNEZC
865   switch (opcode) {
866     case BEQ:
867       opcode = BNE;  // change BEQ to BNE.
868       break;
869     case POP10:
870       opcode = POP30;  // change BEQC to BNEC.
871       break;
872     case POP66:
873       opcode = POP76;  // change BEQZC to BNEZC.
874       break;
875     case BNE:
876       opcode = BEQ;  // change BNE to BEQ.
877       break;
878     case POP30:
879       opcode = POP10;  // change BNEC to BEQC.
880       break;
881     case POP76:
882       opcode = POP66;  // change BNEZC to BEQZC.
883       break;
884     default:
885       UNIMPLEMENTED();
886   }
887   patcher.ChangeBranchCondition(branch_instr, opcode);
888 }
889 }  // namespace internal
890 }  // namespace v8
891 
892 #endif  // V8_TARGET_ARCH_MIPS
893