1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/ic/keyed-store-generic.h"
6 
7 #include "src/compiler/code-assembler.h"
8 #include "src/contexts.h"
9 #include "src/isolate.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 using compiler::Node;
15 
16 class KeyedStoreGenericAssembler : public CodeStubAssembler {
17  public:
18   void KeyedStoreGeneric(const StoreICParameters* p,
19                          LanguageMode language_mode);
20 
21  private:
22   enum UpdateLength {
23     kDontChangeLength,
24     kIncrementLengthByOne,
25     kBumpLengthWithGap
26   };
27 
28   void EmitGenericElementStore(Node* receiver, Node* receiver_map,
29                                Node* instance_type, Node* intptr_index,
30                                Node* value, Node* context, Label* slow);
31 
32   void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
33                                 const StoreICParameters* p, Label* slow);
34 
35   void BranchIfPrototypesHaveNonFastElements(Node* receiver_map,
36                                              Label* non_fast_elements,
37                                              Label* only_fast_elements);
38 
39   void TryRewriteElements(Node* receiver, Node* receiver_map, Node* elements,
40                           Node* native_context, ElementsKind from_kind,
41                           ElementsKind to_kind, Label* bailout);
42 
43   void StoreElementWithCapacity(Node* receiver, Node* receiver_map,
44                                 Node* elements, Node* elements_kind,
45                                 Node* intptr_index, Node* value, Node* context,
46                                 Label* slow, UpdateLength update_length);
47 
48   void MaybeUpdateLengthAndReturn(Node* receiver, Node* index, Node* value,
49                                   UpdateLength update_length);
50 
51   void TryChangeToHoleyMapHelper(Node* receiver, Node* receiver_map,
52                                  Node* native_context, ElementsKind packed_kind,
53                                  ElementsKind holey_kind, Label* done,
54                                  Label* map_mismatch, Label* bailout);
55   void TryChangeToHoleyMap(Node* receiver, Node* receiver_map,
56                            Node* current_elements_kind, Node* context,
57                            ElementsKind packed_kind, Label* bailout);
58   void TryChangeToHoleyMapMulti(Node* receiver, Node* receiver_map,
59                                 Node* current_elements_kind, Node* context,
60                                 ElementsKind packed_kind,
61                                 ElementsKind packed_kind_2, Label* bailout);
62 
63   // Do not add fields, so that this is safe to reinterpret_cast to CSA.
64 };
65 
Generate(CodeStubAssembler * assembler,const CodeStubAssembler::StoreICParameters * p,LanguageMode language_mode)66 void KeyedStoreGenericGenerator::Generate(
67     CodeStubAssembler* assembler, const CodeStubAssembler::StoreICParameters* p,
68     LanguageMode language_mode) {
69   STATIC_ASSERT(sizeof(CodeStubAssembler) ==
70                 sizeof(KeyedStoreGenericAssembler));
71   auto assm = reinterpret_cast<KeyedStoreGenericAssembler*>(assembler);
72   assm->KeyedStoreGeneric(p, language_mode);
73 }
74 
BranchIfPrototypesHaveNonFastElements(Node * receiver_map,Label * non_fast_elements,Label * only_fast_elements)75 void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
76     Node* receiver_map, Label* non_fast_elements, Label* only_fast_elements) {
77   Variable var_map(this, MachineRepresentation::kTagged);
78   var_map.Bind(receiver_map);
79   Label loop_body(this, &var_map);
80   Goto(&loop_body);
81 
82   Bind(&loop_body);
83   {
84     Node* map = var_map.value();
85     Node* prototype = LoadMapPrototype(map);
86     GotoIf(WordEqual(prototype, NullConstant()), only_fast_elements);
87     Node* prototype_map = LoadMap(prototype);
88     var_map.Bind(prototype_map);
89     Node* instance_type = LoadMapInstanceType(prototype_map);
90     STATIC_ASSERT(JS_PROXY_TYPE < JS_OBJECT_TYPE);
91     STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
92     GotoIf(Int32LessThanOrEqual(instance_type,
93                                 Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
94            non_fast_elements);
95     Node* elements_kind = LoadMapElementsKind(prototype_map);
96     STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);
97     GotoIf(Int32LessThanOrEqual(elements_kind,
98                                 Int32Constant(LAST_FAST_ELEMENTS_KIND)),
99            &loop_body);
100     GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
101     Goto(non_fast_elements);
102   }
103 }
104 
TryRewriteElements(Node * receiver,Node * receiver_map,Node * elements,Node * native_context,ElementsKind from_kind,ElementsKind to_kind,Label * bailout)105 void KeyedStoreGenericAssembler::TryRewriteElements(
106     Node* receiver, Node* receiver_map, Node* elements, Node* native_context,
107     ElementsKind from_kind, ElementsKind to_kind, Label* bailout) {
108   DCHECK(IsFastPackedElementsKind(from_kind));
109   ElementsKind holey_from_kind = GetHoleyElementsKind(from_kind);
110   ElementsKind holey_to_kind = GetHoleyElementsKind(to_kind);
111   if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
112     TrapAllocationMemento(receiver, bailout);
113   }
114   Label perform_transition(this), check_holey_map(this);
115   Variable var_target_map(this, MachineType::PointerRepresentation());
116   // Check if the receiver has the default |from_kind| map.
117   {
118     Node* packed_map =
119         LoadContextElement(native_context, Context::ArrayMapIndex(from_kind));
120     GotoIf(WordNotEqual(receiver_map, packed_map), &check_holey_map);
121     var_target_map.Bind(
122         LoadContextElement(native_context, Context::ArrayMapIndex(to_kind)));
123     Goto(&perform_transition);
124   }
125 
126   // Check if the receiver has the default |holey_from_kind| map.
127   Bind(&check_holey_map);
128   {
129     Node* holey_map = LoadContextElement(
130         native_context, Context::ArrayMapIndex(holey_from_kind));
131     GotoIf(WordNotEqual(receiver_map, holey_map), bailout);
132     var_target_map.Bind(LoadContextElement(
133         native_context, Context::ArrayMapIndex(holey_to_kind)));
134     Goto(&perform_transition);
135   }
136 
137   // Found a supported transition target map, perform the transition!
138   Bind(&perform_transition);
139   {
140     if (IsFastDoubleElementsKind(from_kind) !=
141         IsFastDoubleElementsKind(to_kind)) {
142       Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
143       GrowElementsCapacity(receiver, elements, from_kind, to_kind, capacity,
144                            capacity, INTPTR_PARAMETERS, bailout);
145     }
146     StoreObjectField(receiver, JSObject::kMapOffset, var_target_map.value());
147   }
148 }
149 
TryChangeToHoleyMapHelper(Node * receiver,Node * receiver_map,Node * native_context,ElementsKind packed_kind,ElementsKind holey_kind,Label * done,Label * map_mismatch,Label * bailout)150 void KeyedStoreGenericAssembler::TryChangeToHoleyMapHelper(
151     Node* receiver, Node* receiver_map, Node* native_context,
152     ElementsKind packed_kind, ElementsKind holey_kind, Label* done,
153     Label* map_mismatch, Label* bailout) {
154   Node* packed_map =
155       LoadContextElement(native_context, Context::ArrayMapIndex(packed_kind));
156   GotoIf(WordNotEqual(receiver_map, packed_map), map_mismatch);
157   if (AllocationSite::GetMode(packed_kind, holey_kind) ==
158       TRACK_ALLOCATION_SITE) {
159     TrapAllocationMemento(receiver, bailout);
160   }
161   Node* holey_map =
162       LoadContextElement(native_context, Context::ArrayMapIndex(holey_kind));
163   StoreObjectField(receiver, JSObject::kMapOffset, holey_map);
164   Goto(done);
165 }
166 
TryChangeToHoleyMap(Node * receiver,Node * receiver_map,Node * current_elements_kind,Node * context,ElementsKind packed_kind,Label * bailout)167 void KeyedStoreGenericAssembler::TryChangeToHoleyMap(
168     Node* receiver, Node* receiver_map, Node* current_elements_kind,
169     Node* context, ElementsKind packed_kind, Label* bailout) {
170   ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
171   Label already_holey(this);
172 
173   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
174          &already_holey);
175   Node* native_context = LoadNativeContext(context);
176   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
177                             holey_kind, &already_holey, bailout, bailout);
178   Bind(&already_holey);
179 }
180 
TryChangeToHoleyMapMulti(Node * receiver,Node * receiver_map,Node * current_elements_kind,Node * context,ElementsKind packed_kind,ElementsKind packed_kind_2,Label * bailout)181 void KeyedStoreGenericAssembler::TryChangeToHoleyMapMulti(
182     Node* receiver, Node* receiver_map, Node* current_elements_kind,
183     Node* context, ElementsKind packed_kind, ElementsKind packed_kind_2,
184     Label* bailout) {
185   ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
186   ElementsKind holey_kind_2 = GetHoleyElementsKind(packed_kind_2);
187   Label already_holey(this), check_other_kind(this);
188 
189   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
190          &already_holey);
191   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind_2)),
192          &already_holey);
193 
194   Node* native_context = LoadNativeContext(context);
195   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
196                             holey_kind, &already_holey, &check_other_kind,
197                             bailout);
198   Bind(&check_other_kind);
199   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context,
200                             packed_kind_2, holey_kind_2, &already_holey,
201                             bailout, bailout);
202   Bind(&already_holey);
203 }
204 
MaybeUpdateLengthAndReturn(Node * receiver,Node * index,Node * value,UpdateLength update_length)205 void KeyedStoreGenericAssembler::MaybeUpdateLengthAndReturn(
206     Node* receiver, Node* index, Node* value, UpdateLength update_length) {
207   if (update_length != kDontChangeLength) {
208     Node* new_length = SmiTag(IntPtrAdd(index, IntPtrConstant(1)));
209     StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset, new_length,
210                                    MachineRepresentation::kTagged);
211   }
212   Return(value);
213 }
214 
StoreElementWithCapacity(Node * receiver,Node * receiver_map,Node * elements,Node * elements_kind,Node * intptr_index,Node * value,Node * context,Label * slow,UpdateLength update_length)215 void KeyedStoreGenericAssembler::StoreElementWithCapacity(
216     Node* receiver, Node* receiver_map, Node* elements, Node* elements_kind,
217     Node* intptr_index, Node* value, Node* context, Label* slow,
218     UpdateLength update_length) {
219   if (update_length != kDontChangeLength) {
220     CSA_ASSERT(this, Word32Equal(LoadMapInstanceType(receiver_map),
221                                  Int32Constant(JS_ARRAY_TYPE)));
222   }
223   STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
224   const int kHeaderSize = FixedArray::kHeaderSize - kHeapObjectTag;
225 
226   Label check_double_elements(this), check_cow_elements(this);
227   Node* elements_map = LoadMap(elements);
228   GotoIf(WordNotEqual(elements_map, LoadRoot(Heap::kFixedArrayMapRootIndex)),
229          &check_double_elements);
230 
231   // FixedArray backing store -> Smi or object elements.
232   {
233     Node* offset = ElementOffsetFromIndex(intptr_index, FAST_ELEMENTS,
234                                           INTPTR_PARAMETERS, kHeaderSize);
235     // Check if we're about to overwrite the hole. We can safely do that
236     // only if there can be no setters on the prototype chain.
237     // If we know that we're storing beyond the previous array length, we
238     // can skip the hole check (and always assume the hole).
239     {
240       Label hole_check_passed(this);
241       if (update_length == kDontChangeLength) {
242         Node* element = Load(MachineType::AnyTagged(), elements, offset);
243         GotoIf(WordNotEqual(element, TheHoleConstant()), &hole_check_passed);
244       }
245       BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
246                                             &hole_check_passed);
247       Bind(&hole_check_passed);
248     }
249 
250     // Check if the value we're storing matches the elements_kind. Smis
251     // can always be stored.
252     {
253       Label non_smi_value(this);
254       GotoUnless(TaggedIsSmi(value), &non_smi_value);
255       // If we're about to introduce holes, ensure holey elements.
256       if (update_length == kBumpLengthWithGap) {
257         TryChangeToHoleyMapMulti(receiver, receiver_map, elements_kind, context,
258                                  FAST_SMI_ELEMENTS, FAST_ELEMENTS, slow);
259       }
260       StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, offset,
261                           value);
262       MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
263 
264       Bind(&non_smi_value);
265     }
266 
267     // Check if we already have object elements; just do the store if so.
268     {
269       Label must_transition(this);
270       STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
271       STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
272       GotoIf(Int32LessThanOrEqual(elements_kind,
273                                   Int32Constant(FAST_HOLEY_SMI_ELEMENTS)),
274              &must_transition);
275       if (update_length == kBumpLengthWithGap) {
276         TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
277                             FAST_ELEMENTS, slow);
278       }
279       Store(MachineRepresentation::kTagged, elements, offset, value);
280       MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
281 
282       Bind(&must_transition);
283     }
284 
285     // Transition to the required ElementsKind.
286     {
287       Label transition_to_double(this), transition_to_object(this);
288       Node* native_context = LoadNativeContext(context);
289       Branch(WordEqual(LoadMap(value), LoadRoot(Heap::kHeapNumberMapRootIndex)),
290              &transition_to_double, &transition_to_object);
291       Bind(&transition_to_double);
292       {
293         // If we're adding holes at the end, always transition to a holey
294         // elements kind, otherwise try to remain packed.
295         ElementsKind target_kind = update_length == kBumpLengthWithGap
296                                        ? FAST_HOLEY_DOUBLE_ELEMENTS
297                                        : FAST_DOUBLE_ELEMENTS;
298         TryRewriteElements(receiver, receiver_map, elements, native_context,
299                            FAST_SMI_ELEMENTS, target_kind, slow);
300         // Reload migrated elements.
301         Node* double_elements = LoadElements(receiver);
302         Node* double_offset = ElementOffsetFromIndex(
303             intptr_index, FAST_DOUBLE_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
304         // Make sure we do not store signalling NaNs into double arrays.
305         Node* double_value = Float64SilenceNaN(LoadHeapNumberValue(value));
306         StoreNoWriteBarrier(MachineRepresentation::kFloat64, double_elements,
307                             double_offset, double_value);
308         MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
309                                    update_length);
310       }
311 
312       Bind(&transition_to_object);
313       {
314         // If we're adding holes at the end, always transition to a holey
315         // elements kind, otherwise try to remain packed.
316         ElementsKind target_kind = update_length == kBumpLengthWithGap
317                                        ? FAST_HOLEY_ELEMENTS
318                                        : FAST_ELEMENTS;
319         TryRewriteElements(receiver, receiver_map, elements, native_context,
320                            FAST_SMI_ELEMENTS, target_kind, slow);
321         // The elements backing store didn't change, no reload necessary.
322         CSA_ASSERT(this, WordEqual(elements, LoadElements(receiver)));
323         Store(MachineRepresentation::kTagged, elements, offset, value);
324         MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
325                                    update_length);
326       }
327     }
328   }
329 
330   Bind(&check_double_elements);
331   Node* fixed_double_array_map = LoadRoot(Heap::kFixedDoubleArrayMapRootIndex);
332   GotoIf(WordNotEqual(elements_map, fixed_double_array_map),
333          &check_cow_elements);
334   // FixedDoubleArray backing store -> double elements.
335   {
336     Node* offset = ElementOffsetFromIndex(intptr_index, FAST_DOUBLE_ELEMENTS,
337                                           INTPTR_PARAMETERS, kHeaderSize);
338     // Check if we're about to overwrite the hole. We can safely do that
339     // only if there can be no setters on the prototype chain.
340     {
341       Label hole_check_passed(this);
342       // If we know that we're storing beyond the previous array length, we
343       // can skip the hole check (and always assume the hole).
344       if (update_length == kDontChangeLength) {
345         Label found_hole(this);
346         LoadDoubleWithHoleCheck(elements, offset, &found_hole,
347                                 MachineType::None());
348         Goto(&hole_check_passed);
349         Bind(&found_hole);
350       }
351       BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
352                                             &hole_check_passed);
353       Bind(&hole_check_passed);
354     }
355 
356     // Try to store the value as a double.
357     {
358       Label non_number_value(this);
359       Node* double_value = PrepareValueForWrite(value, Representation::Double(),
360                                                 &non_number_value);
361       // Make sure we do not store signalling NaNs into double arrays.
362       double_value = Float64SilenceNaN(double_value);
363       // If we're about to introduce holes, ensure holey elements.
364       if (update_length == kBumpLengthWithGap) {
365         TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
366                             FAST_DOUBLE_ELEMENTS, slow);
367       }
368       StoreNoWriteBarrier(MachineRepresentation::kFloat64, elements, offset,
369                           double_value);
370       MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
371 
372       Bind(&non_number_value);
373     }
374 
375     // Transition to object elements.
376     {
377       Node* native_context = LoadNativeContext(context);
378       ElementsKind target_kind = update_length == kBumpLengthWithGap
379                                      ? FAST_HOLEY_ELEMENTS
380                                      : FAST_ELEMENTS;
381       TryRewriteElements(receiver, receiver_map, elements, native_context,
382                          FAST_DOUBLE_ELEMENTS, target_kind, slow);
383       // Reload migrated elements.
384       Node* fast_elements = LoadElements(receiver);
385       Node* fast_offset = ElementOffsetFromIndex(
386           intptr_index, FAST_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
387       Store(MachineRepresentation::kTagged, fast_elements, fast_offset, value);
388       MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
389     }
390   }
391 
392   Bind(&check_cow_elements);
393   {
394     // TODO(jkummerow): Use GrowElementsCapacity instead of bailing out.
395     Goto(slow);
396   }
397 }
398 
EmitGenericElementStore(Node * receiver,Node * receiver_map,Node * instance_type,Node * intptr_index,Node * value,Node * context,Label * slow)399 void KeyedStoreGenericAssembler::EmitGenericElementStore(
400     Node* receiver, Node* receiver_map, Node* instance_type, Node* intptr_index,
401     Node* value, Node* context, Label* slow) {
402   Label if_in_bounds(this), if_increment_length_by_one(this),
403       if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
404       if_typed_array(this), if_dictionary(this);
405   Node* elements = LoadElements(receiver);
406   Node* elements_kind = LoadMapElementsKind(receiver_map);
407   GotoIf(
408       Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
409       &if_nonfast);
410 
411   Label if_array(this);
412   GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)), &if_array);
413   {
414     Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
415     Branch(UintPtrLessThan(intptr_index, capacity), &if_in_bounds, &if_grow);
416   }
417   Bind(&if_array);
418   {
419     Node* length = SmiUntag(LoadJSArrayLength(receiver));
420     GotoIf(UintPtrLessThan(intptr_index, length), &if_in_bounds);
421     Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
422     GotoIf(UintPtrGreaterThanOrEqual(intptr_index, capacity), &if_grow);
423     Branch(WordEqual(intptr_index, length), &if_increment_length_by_one,
424            &if_bump_length_with_gap);
425   }
426 
427   Bind(&if_in_bounds);
428   {
429     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
430                              intptr_index, value, context, slow,
431                              kDontChangeLength);
432   }
433 
434   Bind(&if_increment_length_by_one);
435   {
436     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
437                              intptr_index, value, context, slow,
438                              kIncrementLengthByOne);
439   }
440 
441   Bind(&if_bump_length_with_gap);
442   {
443     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
444                              intptr_index, value, context, slow,
445                              kBumpLengthWithGap);
446   }
447 
448   // Out-of-capacity accesses (index >= capacity) jump here. Additionally,
449   // an ElementsKind transition might be necessary.
450   Bind(&if_grow);
451   {
452     Comment("Grow backing store");
453     // TODO(jkummerow): Support inline backing store growth.
454     Goto(slow);
455   }
456 
457   // Any ElementsKind > LAST_FAST_ELEMENTS_KIND jumps here for further dispatch.
458   Bind(&if_nonfast);
459   {
460     STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
461     GotoIf(Int32GreaterThanOrEqual(
462                elements_kind,
463                Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
464            &if_typed_array);
465     GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
466            &if_dictionary);
467     Goto(slow);
468   }
469 
470   Bind(&if_dictionary);
471   {
472     Comment("Dictionary");
473     // TODO(jkummerow): Support storing to dictionary elements.
474     Goto(slow);
475   }
476 
477   Bind(&if_typed_array);
478   {
479     Comment("Typed array");
480     // TODO(jkummerow): Support typed arrays.
481     Goto(slow);
482   }
483 }
484 
EmitGenericPropertyStore(Node * receiver,Node * receiver_map,const StoreICParameters * p,Label * slow)485 void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
486     Node* receiver, Node* receiver_map, const StoreICParameters* p,
487     Label* slow) {
488   Comment("stub cache probe");
489   // TODO(jkummerow): Don't rely on the stub cache as much.
490   // - existing properties can be overwritten inline (unless readonly).
491   // - for dictionary mode receivers, we can even add properties inline
492   //   (unless the prototype chain prevents it).
493   Variable var_handler(this, MachineRepresentation::kTagged);
494   Label found_handler(this, &var_handler), stub_cache_miss(this);
495   TryProbeStubCache(isolate()->store_stub_cache(), receiver, p->name,
496                     &found_handler, &var_handler, &stub_cache_miss);
497   Bind(&found_handler);
498   {
499     Comment("KeyedStoreGeneric found handler");
500     HandleStoreICHandlerCase(p, var_handler.value(), slow);
501   }
502   Bind(&stub_cache_miss);
503   {
504     Comment("KeyedStoreGeneric_miss");
505     TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
506                     p->vector, p->receiver, p->name);
507   }
508 }
509 
KeyedStoreGeneric(const StoreICParameters * p,LanguageMode language_mode)510 void KeyedStoreGenericAssembler::KeyedStoreGeneric(const StoreICParameters* p,
511                                                    LanguageMode language_mode) {
512   Variable var_index(this, MachineType::PointerRepresentation());
513   Label if_index(this), if_unique_name(this), slow(this);
514 
515   Node* receiver = p->receiver;
516   GotoIf(TaggedIsSmi(receiver), &slow);
517   Node* receiver_map = LoadMap(receiver);
518   Node* instance_type = LoadMapInstanceType(receiver_map);
519   // Receivers requiring non-standard element accesses (interceptors, access
520   // checks, strings and string wrappers, proxies) are handled in the runtime.
521   GotoIf(Int32LessThanOrEqual(instance_type,
522                               Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
523          &slow);
524 
525   TryToName(p->name, &if_index, &var_index, &if_unique_name, &slow);
526 
527   Bind(&if_index);
528   {
529     Comment("integer index");
530     EmitGenericElementStore(receiver, receiver_map, instance_type,
531                             var_index.value(), p->value, p->context, &slow);
532   }
533 
534   Bind(&if_unique_name);
535   {
536     Comment("key is unique name");
537     EmitGenericPropertyStore(receiver, receiver_map, p, &slow);
538   }
539 
540   Bind(&slow);
541   {
542     Comment("KeyedStoreGeneric_slow");
543     TailCallRuntime(Runtime::kSetProperty, p->context, p->receiver, p->name,
544                     p->value, SmiConstant(language_mode));
545   }
546 }
547 
548 }  // namespace internal
549 }  // namespace v8
550