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/code-factory.h"
8#include "src/code-stub-assembler.h"
9#include "src/contexts.h"
10#include "src/ic/accessor-assembler.h"
11#include "src/interface-descriptors.h"
12#include "src/isolate.h"
13#include "src/objects-inl.h"
14
15namespace v8 {
16namespace internal {
17
18using compiler::Node;
19
20class KeyedStoreGenericAssembler : public AccessorAssembler {
21 public:
22  explicit KeyedStoreGenericAssembler(compiler::CodeAssemblerState* state)
23      : AccessorAssembler(state) {}
24
25  void KeyedStoreGeneric(LanguageMode language_mode);
26
27 private:
28  enum UpdateLength {
29    kDontChangeLength,
30    kIncrementLengthByOne,
31    kBumpLengthWithGap
32  };
33
34  void EmitGenericElementStore(Node* receiver, Node* receiver_map,
35                               Node* instance_type, Node* intptr_index,
36                               Node* value, Node* context, Label* slow);
37
38  void EmitGenericPropertyStore(Node* receiver, Node* receiver_map,
39                                const StoreICParameters* p, Label* slow,
40                                LanguageMode language_mode);
41
42  void BranchIfPrototypesHaveNonFastElements(Node* receiver_map,
43                                             Label* non_fast_elements,
44                                             Label* only_fast_elements);
45
46  void TryRewriteElements(Node* receiver, Node* receiver_map, Node* elements,
47                          Node* native_context, ElementsKind from_kind,
48                          ElementsKind to_kind, Label* bailout);
49
50  void StoreElementWithCapacity(Node* receiver, Node* receiver_map,
51                                Node* elements, Node* elements_kind,
52                                Node* intptr_index, Node* value, Node* context,
53                                Label* slow, UpdateLength update_length);
54
55  void MaybeUpdateLengthAndReturn(Node* receiver, Node* index, Node* value,
56                                  UpdateLength update_length);
57
58  void TryChangeToHoleyMapHelper(Node* receiver, Node* receiver_map,
59                                 Node* native_context, ElementsKind packed_kind,
60                                 ElementsKind holey_kind, Label* done,
61                                 Label* map_mismatch, Label* bailout);
62  void TryChangeToHoleyMap(Node* receiver, Node* receiver_map,
63                           Node* current_elements_kind, Node* context,
64                           ElementsKind packed_kind, Label* bailout);
65  void TryChangeToHoleyMapMulti(Node* receiver, Node* receiver_map,
66                                Node* current_elements_kind, Node* context,
67                                ElementsKind packed_kind,
68                                ElementsKind packed_kind_2, Label* bailout);
69
70  void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
71  void LookupPropertyOnPrototypeChain(Node* receiver_map, Node* name,
72                                      Label* accessor,
73                                      Variable* var_accessor_pair,
74                                      Variable* var_accessor_holder,
75                                      Label* readonly, Label* bailout);
76
77  void CheckFieldType(Node* descriptors, Node* name_index, Node* representation,
78                      Node* value, Label* bailout);
79  void OverwriteExistingFastProperty(Node* object, Node* object_map,
80                                     Node* properties, Node* descriptors,
81                                     Node* descriptor_name_index, Node* details,
82                                     Node* value, Label* slow);
83};
84
85void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state,
86                                          LanguageMode language_mode) {
87  KeyedStoreGenericAssembler assembler(state);
88  assembler.KeyedStoreGeneric(language_mode);
89}
90
91void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
92    Node* receiver_map, Label* non_fast_elements, Label* only_fast_elements) {
93  Variable var_map(this, MachineRepresentation::kTagged);
94  var_map.Bind(receiver_map);
95  Label loop_body(this, &var_map);
96  Goto(&loop_body);
97
98  Bind(&loop_body);
99  {
100    Node* map = var_map.value();
101    Node* prototype = LoadMapPrototype(map);
102    GotoIf(WordEqual(prototype, NullConstant()), only_fast_elements);
103    Node* prototype_map = LoadMap(prototype);
104    var_map.Bind(prototype_map);
105    Node* instance_type = LoadMapInstanceType(prototype_map);
106    STATIC_ASSERT(JS_PROXY_TYPE < JS_OBJECT_TYPE);
107    STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
108    GotoIf(Int32LessThanOrEqual(instance_type,
109                                Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
110           non_fast_elements);
111    Node* elements_kind = LoadMapElementsKind(prototype_map);
112    STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);
113    GotoIf(IsFastElementsKind(elements_kind), &loop_body);
114    GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
115    Goto(non_fast_elements);
116  }
117}
118
119void KeyedStoreGenericAssembler::TryRewriteElements(
120    Node* receiver, Node* receiver_map, Node* elements, Node* native_context,
121    ElementsKind from_kind, ElementsKind to_kind, Label* bailout) {
122  DCHECK(IsFastPackedElementsKind(from_kind));
123  ElementsKind holey_from_kind = GetHoleyElementsKind(from_kind);
124  ElementsKind holey_to_kind = GetHoleyElementsKind(to_kind);
125  if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
126    TrapAllocationMemento(receiver, bailout);
127  }
128  Label perform_transition(this), check_holey_map(this);
129  Variable var_target_map(this, MachineRepresentation::kTagged);
130  // Check if the receiver has the default |from_kind| map.
131  {
132    Node* packed_map =
133        LoadContextElement(native_context, Context::ArrayMapIndex(from_kind));
134    GotoIf(WordNotEqual(receiver_map, packed_map), &check_holey_map);
135    var_target_map.Bind(
136        LoadContextElement(native_context, Context::ArrayMapIndex(to_kind)));
137    Goto(&perform_transition);
138  }
139
140  // Check if the receiver has the default |holey_from_kind| map.
141  Bind(&check_holey_map);
142  {
143    Node* holey_map = LoadContextElement(
144        native_context, Context::ArrayMapIndex(holey_from_kind));
145    GotoIf(WordNotEqual(receiver_map, holey_map), bailout);
146    var_target_map.Bind(LoadContextElement(
147        native_context, Context::ArrayMapIndex(holey_to_kind)));
148    Goto(&perform_transition);
149  }
150
151  // Found a supported transition target map, perform the transition!
152  Bind(&perform_transition);
153  {
154    if (IsFastDoubleElementsKind(from_kind) !=
155        IsFastDoubleElementsKind(to_kind)) {
156      Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
157      GrowElementsCapacity(receiver, elements, from_kind, to_kind, capacity,
158                           capacity, INTPTR_PARAMETERS, bailout);
159    }
160    StoreMap(receiver, var_target_map.value());
161  }
162}
163
164void KeyedStoreGenericAssembler::TryChangeToHoleyMapHelper(
165    Node* receiver, Node* receiver_map, Node* native_context,
166    ElementsKind packed_kind, ElementsKind holey_kind, Label* done,
167    Label* map_mismatch, Label* bailout) {
168  Node* packed_map =
169      LoadContextElement(native_context, Context::ArrayMapIndex(packed_kind));
170  GotoIf(WordNotEqual(receiver_map, packed_map), map_mismatch);
171  if (AllocationSite::GetMode(packed_kind, holey_kind) ==
172      TRACK_ALLOCATION_SITE) {
173    TrapAllocationMemento(receiver, bailout);
174  }
175  Node* holey_map =
176      LoadContextElement(native_context, Context::ArrayMapIndex(holey_kind));
177  StoreMap(receiver, holey_map);
178  Goto(done);
179}
180
181void KeyedStoreGenericAssembler::TryChangeToHoleyMap(
182    Node* receiver, Node* receiver_map, Node* current_elements_kind,
183    Node* context, ElementsKind packed_kind, Label* bailout) {
184  ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
185  Label already_holey(this);
186
187  GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
188         &already_holey);
189  Node* native_context = LoadNativeContext(context);
190  TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
191                            holey_kind, &already_holey, bailout, bailout);
192  Bind(&already_holey);
193}
194
195void KeyedStoreGenericAssembler::TryChangeToHoleyMapMulti(
196    Node* receiver, Node* receiver_map, Node* current_elements_kind,
197    Node* context, ElementsKind packed_kind, ElementsKind packed_kind_2,
198    Label* bailout) {
199  ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
200  ElementsKind holey_kind_2 = GetHoleyElementsKind(packed_kind_2);
201  Label already_holey(this), check_other_kind(this);
202
203  GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
204         &already_holey);
205  GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind_2)),
206         &already_holey);
207
208  Node* native_context = LoadNativeContext(context);
209  TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
210                            holey_kind, &already_holey, &check_other_kind,
211                            bailout);
212  Bind(&check_other_kind);
213  TryChangeToHoleyMapHelper(receiver, receiver_map, native_context,
214                            packed_kind_2, holey_kind_2, &already_holey,
215                            bailout, bailout);
216  Bind(&already_holey);
217}
218
219void KeyedStoreGenericAssembler::MaybeUpdateLengthAndReturn(
220    Node* receiver, Node* index, Node* value, UpdateLength update_length) {
221  if (update_length != kDontChangeLength) {
222    Node* new_length = SmiTag(IntPtrAdd(index, IntPtrConstant(1)));
223    StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset, new_length,
224                                   MachineRepresentation::kTagged);
225  }
226  Return(value);
227}
228
229void KeyedStoreGenericAssembler::StoreElementWithCapacity(
230    Node* receiver, Node* receiver_map, Node* elements, Node* elements_kind,
231    Node* intptr_index, Node* value, Node* context, Label* slow,
232    UpdateLength update_length) {
233  if (update_length != kDontChangeLength) {
234    CSA_ASSERT(this, Word32Equal(LoadMapInstanceType(receiver_map),
235                                 Int32Constant(JS_ARRAY_TYPE)));
236    // Check if the length property is writable. The fast check is only
237    // supported for fast properties.
238    GotoIf(IsDictionaryMap(receiver_map), slow);
239    // The length property is non-configurable, so it's guaranteed to always
240    // be the first property.
241    Node* descriptors = LoadMapDescriptors(receiver_map);
242    Node* details =
243        LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0));
244    GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask), slow);
245  }
246  STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
247  const int kHeaderSize = FixedArray::kHeaderSize - kHeapObjectTag;
248
249  Label check_double_elements(this), check_cow_elements(this);
250  Node* elements_map = LoadMap(elements);
251  GotoIf(WordNotEqual(elements_map, LoadRoot(Heap::kFixedArrayMapRootIndex)),
252         &check_double_elements);
253
254  // FixedArray backing store -> Smi or object elements.
255  {
256    Node* offset = ElementOffsetFromIndex(intptr_index, FAST_ELEMENTS,
257                                          INTPTR_PARAMETERS, kHeaderSize);
258    // Check if we're about to overwrite the hole. We can safely do that
259    // only if there can be no setters on the prototype chain.
260    // If we know that we're storing beyond the previous array length, we
261    // can skip the hole check (and always assume the hole).
262    {
263      Label hole_check_passed(this);
264      if (update_length == kDontChangeLength) {
265        Node* element = Load(MachineType::AnyTagged(), elements, offset);
266        GotoIf(WordNotEqual(element, TheHoleConstant()), &hole_check_passed);
267      }
268      BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
269                                            &hole_check_passed);
270      Bind(&hole_check_passed);
271    }
272
273    // Check if the value we're storing matches the elements_kind. Smis
274    // can always be stored.
275    {
276      Label non_smi_value(this);
277      GotoIfNot(TaggedIsSmi(value), &non_smi_value);
278      // If we're about to introduce holes, ensure holey elements.
279      if (update_length == kBumpLengthWithGap) {
280        TryChangeToHoleyMapMulti(receiver, receiver_map, elements_kind, context,
281                                 FAST_SMI_ELEMENTS, FAST_ELEMENTS, slow);
282      }
283      StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, offset,
284                          value);
285      MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
286
287      Bind(&non_smi_value);
288    }
289
290    // Check if we already have object elements; just do the store if so.
291    {
292      Label must_transition(this);
293      STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
294      STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
295      GotoIf(Int32LessThanOrEqual(elements_kind,
296                                  Int32Constant(FAST_HOLEY_SMI_ELEMENTS)),
297             &must_transition);
298      if (update_length == kBumpLengthWithGap) {
299        TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
300                            FAST_ELEMENTS, slow);
301      }
302      Store(elements, offset, value);
303      MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
304
305      Bind(&must_transition);
306    }
307
308    // Transition to the required ElementsKind.
309    {
310      Label transition_to_double(this), transition_to_object(this);
311      Node* native_context = LoadNativeContext(context);
312      Branch(WordEqual(LoadMap(value), LoadRoot(Heap::kHeapNumberMapRootIndex)),
313             &transition_to_double, &transition_to_object);
314      Bind(&transition_to_double);
315      {
316        // If we're adding holes at the end, always transition to a holey
317        // elements kind, otherwise try to remain packed.
318        ElementsKind target_kind = update_length == kBumpLengthWithGap
319                                       ? FAST_HOLEY_DOUBLE_ELEMENTS
320                                       : FAST_DOUBLE_ELEMENTS;
321        TryRewriteElements(receiver, receiver_map, elements, native_context,
322                           FAST_SMI_ELEMENTS, target_kind, slow);
323        // Reload migrated elements.
324        Node* double_elements = LoadElements(receiver);
325        Node* double_offset = ElementOffsetFromIndex(
326            intptr_index, FAST_DOUBLE_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
327        // Make sure we do not store signalling NaNs into double arrays.
328        Node* double_value = Float64SilenceNaN(LoadHeapNumberValue(value));
329        StoreNoWriteBarrier(MachineRepresentation::kFloat64, double_elements,
330                            double_offset, double_value);
331        MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
332                                   update_length);
333      }
334
335      Bind(&transition_to_object);
336      {
337        // If we're adding holes at the end, always transition to a holey
338        // elements kind, otherwise try to remain packed.
339        ElementsKind target_kind = update_length == kBumpLengthWithGap
340                                       ? FAST_HOLEY_ELEMENTS
341                                       : FAST_ELEMENTS;
342        TryRewriteElements(receiver, receiver_map, elements, native_context,
343                           FAST_SMI_ELEMENTS, target_kind, slow);
344        // The elements backing store didn't change, no reload necessary.
345        CSA_ASSERT(this, WordEqual(elements, LoadElements(receiver)));
346        Store(elements, offset, value);
347        MaybeUpdateLengthAndReturn(receiver, intptr_index, value,
348                                   update_length);
349      }
350    }
351  }
352
353  Bind(&check_double_elements);
354  Node* fixed_double_array_map = LoadRoot(Heap::kFixedDoubleArrayMapRootIndex);
355  GotoIf(WordNotEqual(elements_map, fixed_double_array_map),
356         &check_cow_elements);
357  // FixedDoubleArray backing store -> double elements.
358  {
359    Node* offset = ElementOffsetFromIndex(intptr_index, FAST_DOUBLE_ELEMENTS,
360                                          INTPTR_PARAMETERS, kHeaderSize);
361    // Check if we're about to overwrite the hole. We can safely do that
362    // only if there can be no setters on the prototype chain.
363    {
364      Label hole_check_passed(this);
365      // If we know that we're storing beyond the previous array length, we
366      // can skip the hole check (and always assume the hole).
367      if (update_length == kDontChangeLength) {
368        Label found_hole(this);
369        LoadDoubleWithHoleCheck(elements, offset, &found_hole,
370                                MachineType::None());
371        Goto(&hole_check_passed);
372        Bind(&found_hole);
373      }
374      BranchIfPrototypesHaveNonFastElements(receiver_map, slow,
375                                            &hole_check_passed);
376      Bind(&hole_check_passed);
377    }
378
379    // Try to store the value as a double.
380    {
381      Label non_number_value(this);
382      Node* double_value = TryTaggedToFloat64(value, &non_number_value);
383
384      // Make sure we do not store signalling NaNs into double arrays.
385      double_value = Float64SilenceNaN(double_value);
386      // If we're about to introduce holes, ensure holey elements.
387      if (update_length == kBumpLengthWithGap) {
388        TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
389                            FAST_DOUBLE_ELEMENTS, slow);
390      }
391      StoreNoWriteBarrier(MachineRepresentation::kFloat64, elements, offset,
392                          double_value);
393      MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
394
395      Bind(&non_number_value);
396    }
397
398    // Transition to object elements.
399    {
400      Node* native_context = LoadNativeContext(context);
401      ElementsKind target_kind = update_length == kBumpLengthWithGap
402                                     ? FAST_HOLEY_ELEMENTS
403                                     : FAST_ELEMENTS;
404      TryRewriteElements(receiver, receiver_map, elements, native_context,
405                         FAST_DOUBLE_ELEMENTS, target_kind, slow);
406      // Reload migrated elements.
407      Node* fast_elements = LoadElements(receiver);
408      Node* fast_offset = ElementOffsetFromIndex(
409          intptr_index, FAST_ELEMENTS, INTPTR_PARAMETERS, kHeaderSize);
410      Store(fast_elements, fast_offset, value);
411      MaybeUpdateLengthAndReturn(receiver, intptr_index, value, update_length);
412    }
413  }
414
415  Bind(&check_cow_elements);
416  {
417    // TODO(jkummerow): Use GrowElementsCapacity instead of bailing out.
418    Goto(slow);
419  }
420}
421
422void KeyedStoreGenericAssembler::EmitGenericElementStore(
423    Node* receiver, Node* receiver_map, Node* instance_type, Node* intptr_index,
424    Node* value, Node* context, Label* slow) {
425  Label if_fast(this), if_in_bounds(this), if_increment_length_by_one(this),
426      if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
427      if_typed_array(this), if_dictionary(this);
428  Node* elements = LoadElements(receiver);
429  Node* elements_kind = LoadMapElementsKind(receiver_map);
430  Branch(IsFastElementsKind(elements_kind), &if_fast, &if_nonfast);
431  Bind(&if_fast);
432
433  Label if_array(this);
434  GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)), &if_array);
435  {
436    Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
437    Branch(UintPtrLessThan(intptr_index, capacity), &if_in_bounds, &if_grow);
438  }
439  Bind(&if_array);
440  {
441    Node* length = SmiUntag(LoadJSArrayLength(receiver));
442    GotoIf(UintPtrLessThan(intptr_index, length), &if_in_bounds);
443    Node* capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
444    GotoIf(UintPtrGreaterThanOrEqual(intptr_index, capacity), &if_grow);
445    Branch(WordEqual(intptr_index, length), &if_increment_length_by_one,
446           &if_bump_length_with_gap);
447  }
448
449  Bind(&if_in_bounds);
450  {
451    StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
452                             intptr_index, value, context, slow,
453                             kDontChangeLength);
454  }
455
456  Bind(&if_increment_length_by_one);
457  {
458    StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
459                             intptr_index, value, context, slow,
460                             kIncrementLengthByOne);
461  }
462
463  Bind(&if_bump_length_with_gap);
464  {
465    StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
466                             intptr_index, value, context, slow,
467                             kBumpLengthWithGap);
468  }
469
470  // Out-of-capacity accesses (index >= capacity) jump here. Additionally,
471  // an ElementsKind transition might be necessary.
472  // The index can also be negative at this point! Jump to the runtime in that
473  // case to convert it to a named property.
474  Bind(&if_grow);
475  {
476    Comment("Grow backing store");
477    // TODO(jkummerow): Support inline backing store growth.
478    Goto(slow);
479  }
480
481  // Any ElementsKind > LAST_FAST_ELEMENTS_KIND jumps here for further dispatch.
482  Bind(&if_nonfast);
483  {
484    STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
485    GotoIf(Int32GreaterThanOrEqual(
486               elements_kind,
487               Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
488           &if_typed_array);
489    GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
490           &if_dictionary);
491    Goto(slow);
492  }
493
494  Bind(&if_dictionary);
495  {
496    Comment("Dictionary");
497    // TODO(jkummerow): Support storing to dictionary elements.
498    Goto(slow);
499  }
500
501  Bind(&if_typed_array);
502  {
503    Comment("Typed array");
504    // TODO(jkummerow): Support typed arrays.
505    Goto(slow);
506  }
507}
508
509void KeyedStoreGenericAssembler::JumpIfDataProperty(Node* details,
510                                                    Label* writable,
511                                                    Label* readonly) {
512  // Accessor properties never have the READ_ONLY attribute set.
513  GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
514         readonly);
515  Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
516  GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
517  // Fall through if it's an accessor property.
518}
519
520void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
521    Node* receiver_map, Node* name, Label* accessor,
522    Variable* var_accessor_pair, Variable* var_accessor_holder, Label* readonly,
523    Label* bailout) {
524  Label ok_to_write(this);
525  Variable var_holder(this, MachineRepresentation::kTagged);
526  var_holder.Bind(LoadMapPrototype(receiver_map));
527  Variable var_holder_map(this, MachineRepresentation::kTagged);
528  var_holder_map.Bind(LoadMap(var_holder.value()));
529
530  Variable* merged_variables[] = {&var_holder, &var_holder_map};
531  Label loop(this, arraysize(merged_variables), merged_variables);
532  Goto(&loop);
533  Bind(&loop);
534  {
535    Node* holder = var_holder.value();
536    Node* holder_map = var_holder_map.value();
537    Node* instance_type = LoadMapInstanceType(holder_map);
538    Label next_proto(this);
539    {
540      Label found(this), found_fast(this), found_dict(this), found_global(this);
541      Variable var_meta_storage(this, MachineRepresentation::kTagged);
542      Variable var_entry(this, MachineType::PointerRepresentation());
543      TryLookupProperty(holder, holder_map, instance_type, name, &found_fast,
544                        &found_dict, &found_global, &var_meta_storage,
545                        &var_entry, &next_proto, bailout);
546      Bind(&found_fast);
547      {
548        Node* descriptors = var_meta_storage.value();
549        Node* name_index = var_entry.value();
550        Node* details =
551            LoadDetailsByKeyIndex<DescriptorArray>(descriptors, name_index);
552        JumpIfDataProperty(details, &ok_to_write, readonly);
553
554        // Accessor case.
555        // TODO(jkummerow): Implement a trimmed-down LoadAccessorFromFastObject.
556        Variable var_details(this, MachineRepresentation::kWord32);
557        LoadPropertyFromFastObject(holder, holder_map, descriptors, name_index,
558                                   &var_details, var_accessor_pair);
559        var_accessor_holder->Bind(holder);
560        Goto(accessor);
561      }
562
563      Bind(&found_dict);
564      {
565        Node* dictionary = var_meta_storage.value();
566        Node* entry = var_entry.value();
567        Node* details =
568            LoadDetailsByKeyIndex<NameDictionary>(dictionary, entry);
569        JumpIfDataProperty(details, &ok_to_write, readonly);
570
571        // Accessor case.
572        var_accessor_pair->Bind(
573            LoadValueByKeyIndex<NameDictionary>(dictionary, entry));
574        var_accessor_holder->Bind(holder);
575        Goto(accessor);
576      }
577
578      Bind(&found_global);
579      {
580        Node* dictionary = var_meta_storage.value();
581        Node* entry = var_entry.value();
582        Node* property_cell =
583            LoadValueByKeyIndex<GlobalDictionary>(dictionary, entry);
584        Node* value =
585            LoadObjectField(property_cell, PropertyCell::kValueOffset);
586        GotoIf(WordEqual(value, TheHoleConstant()), &next_proto);
587        Node* details = LoadAndUntagToWord32ObjectField(
588            property_cell, PropertyCell::kDetailsOffset);
589        JumpIfDataProperty(details, &ok_to_write, readonly);
590
591        // Accessor case.
592        var_accessor_pair->Bind(value);
593        var_accessor_holder->Bind(holder);
594        Goto(accessor);
595      }
596    }
597
598    Bind(&next_proto);
599    // Bailout if it can be an integer indexed exotic case.
600    GotoIf(Word32Equal(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
601           bailout);
602    Node* proto = LoadMapPrototype(holder_map);
603    GotoIf(WordEqual(proto, NullConstant()), &ok_to_write);
604    var_holder.Bind(proto);
605    var_holder_map.Bind(LoadMap(proto));
606    Goto(&loop);
607  }
608  Bind(&ok_to_write);
609}
610
611void KeyedStoreGenericAssembler::CheckFieldType(Node* descriptors,
612                                                Node* name_index,
613                                                Node* representation,
614                                                Node* value, Label* bailout) {
615  Label r_smi(this), r_double(this), r_heapobject(this), all_fine(this);
616  // Ignore FLAG_track_fields etc. and always emit code for all checks,
617  // because this builtin is part of the snapshot and therefore should
618  // be flag independent.
619  GotoIf(Word32Equal(representation, Int32Constant(Representation::kSmi)),
620         &r_smi);
621  GotoIf(Word32Equal(representation, Int32Constant(Representation::kDouble)),
622         &r_double);
623  GotoIf(
624      Word32Equal(representation, Int32Constant(Representation::kHeapObject)),
625      &r_heapobject);
626  GotoIf(Word32Equal(representation, Int32Constant(Representation::kNone)),
627         bailout);
628  CSA_ASSERT(this, Word32Equal(representation,
629                               Int32Constant(Representation::kTagged)));
630  Goto(&all_fine);
631
632  Bind(&r_smi);
633  { Branch(TaggedIsSmi(value), &all_fine, bailout); }
634
635  Bind(&r_double);
636  {
637    GotoIf(TaggedIsSmi(value), &all_fine);
638    Node* value_map = LoadMap(value);
639    // While supporting mutable HeapNumbers would be straightforward, such
640    // objects should not end up here anyway.
641    CSA_ASSERT(this,
642               WordNotEqual(value_map,
643                            LoadRoot(Heap::kMutableHeapNumberMapRootIndex)));
644    Branch(IsHeapNumberMap(value_map), &all_fine, bailout);
645  }
646
647  Bind(&r_heapobject);
648  {
649    GotoIf(TaggedIsSmi(value), bailout);
650    Node* field_type =
651        LoadValueByKeyIndex<DescriptorArray>(descriptors, name_index);
652    intptr_t kNoneType = reinterpret_cast<intptr_t>(FieldType::None());
653    intptr_t kAnyType = reinterpret_cast<intptr_t>(FieldType::Any());
654    // FieldType::None can't hold any value.
655    GotoIf(WordEqual(field_type, IntPtrConstant(kNoneType)), bailout);
656    // FieldType::Any can hold any value.
657    GotoIf(WordEqual(field_type, IntPtrConstant(kAnyType)), &all_fine);
658    CSA_ASSERT(this, IsWeakCell(field_type));
659    // Cleared WeakCells count as FieldType::None, which can't hold any value.
660    field_type = LoadWeakCellValue(field_type, bailout);
661    // FieldType::Class(...) performs a map check.
662    CSA_ASSERT(this, IsMap(field_type));
663    Branch(WordEqual(LoadMap(value), field_type), &all_fine, bailout);
664  }
665
666  Bind(&all_fine);
667}
668
669void KeyedStoreGenericAssembler::OverwriteExistingFastProperty(
670    Node* object, Node* object_map, Node* properties, Node* descriptors,
671    Node* descriptor_name_index, Node* details, Node* value, Label* slow) {
672  // Properties in descriptors can't be overwritten without map transition.
673  GotoIf(Word32NotEqual(DecodeWord32<PropertyDetails::LocationField>(details),
674                        Int32Constant(kField)),
675         slow);
676
677  if (FLAG_track_constant_fields) {
678    // TODO(ishell): Taking the slow path is not necessary if new and old
679    // values are identical.
680    GotoIf(Word32Equal(DecodeWord32<PropertyDetails::ConstnessField>(details),
681                       Int32Constant(kConst)),
682           slow);
683  }
684
685  Label done(this);
686  Node* representation =
687      DecodeWord32<PropertyDetails::RepresentationField>(details);
688
689  CheckFieldType(descriptors, descriptor_name_index, representation, value,
690                 slow);
691  Node* field_index =
692      DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details);
693  Node* inobject_properties = LoadMapInobjectProperties(object_map);
694
695  Label inobject(this), backing_store(this);
696  Branch(UintPtrLessThan(field_index, inobject_properties), &inobject,
697         &backing_store);
698
699  Bind(&inobject);
700  {
701    Node* field_offset =
702        IntPtrMul(IntPtrSub(LoadMapInstanceSize(object_map),
703                            IntPtrSub(inobject_properties, field_index)),
704                  IntPtrConstant(kPointerSize));
705    Label tagged_rep(this), double_rep(this);
706    Branch(Word32Equal(representation, Int32Constant(Representation::kDouble)),
707           &double_rep, &tagged_rep);
708    Bind(&double_rep);
709    {
710      Node* double_value = ChangeNumberToFloat64(value);
711      if (FLAG_unbox_double_fields) {
712        StoreObjectFieldNoWriteBarrier(object, field_offset, double_value,
713                                       MachineRepresentation::kFloat64);
714      } else {
715        Node* mutable_heap_number = LoadObjectField(object, field_offset);
716        StoreHeapNumberValue(mutable_heap_number, double_value);
717      }
718      Goto(&done);
719    }
720
721    Bind(&tagged_rep);
722    {
723      StoreObjectField(object, field_offset, value);
724      Goto(&done);
725    }
726  }
727
728  Bind(&backing_store);
729  {
730    Node* backing_store_index = IntPtrSub(field_index, inobject_properties);
731    Label tagged_rep(this), double_rep(this);
732    Branch(Word32Equal(representation, Int32Constant(Representation::kDouble)),
733           &double_rep, &tagged_rep);
734    Bind(&double_rep);
735    {
736      Node* double_value = ChangeNumberToFloat64(value);
737      Node* mutable_heap_number =
738          LoadFixedArrayElement(properties, backing_store_index);
739      StoreHeapNumberValue(mutable_heap_number, double_value);
740      Goto(&done);
741    }
742    Bind(&tagged_rep);
743    {
744      StoreFixedArrayElement(properties, backing_store_index, value);
745      Goto(&done);
746    }
747  }
748  Bind(&done);
749}
750
751void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
752    Node* receiver, Node* receiver_map, const StoreICParameters* p, Label* slow,
753    LanguageMode language_mode) {
754  Variable var_accessor_pair(this, MachineRepresentation::kTagged);
755  Variable var_accessor_holder(this, MachineRepresentation::kTagged);
756  Label stub_cache(this), fast_properties(this), dictionary_properties(this),
757      accessor(this), readonly(this);
758  Node* properties = LoadProperties(receiver);
759  Node* properties_map = LoadMap(properties);
760  Branch(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
761         &dictionary_properties, &fast_properties);
762
763  Bind(&fast_properties);
764  {
765    Comment("fast property store");
766    Node* bitfield3 = LoadMapBitField3(receiver_map);
767    Node* descriptors = LoadMapDescriptors(receiver_map);
768    Label descriptor_found(this);
769    Variable var_name_index(this, MachineType::PointerRepresentation());
770    // TODO(jkummerow): Maybe look for existing map transitions?
771    Label* notfound = &stub_cache;
772    DescriptorLookup(p->name, descriptors, bitfield3, &descriptor_found,
773                     &var_name_index, notfound);
774
775    Bind(&descriptor_found);
776    {
777      Node* name_index = var_name_index.value();
778      Node* details =
779          LoadDetailsByKeyIndex<DescriptorArray>(descriptors, name_index);
780      Label data_property(this);
781      JumpIfDataProperty(details, &data_property, &readonly);
782
783      // Accessor case.
784      // TODO(jkummerow): Implement a trimmed-down LoadAccessorFromFastObject.
785      Variable var_details(this, MachineRepresentation::kWord32);
786      LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
787                                 name_index, &var_details, &var_accessor_pair);
788      var_accessor_holder.Bind(receiver);
789      Goto(&accessor);
790
791      Bind(&data_property);
792      {
793        OverwriteExistingFastProperty(receiver, receiver_map, properties,
794                                      descriptors, name_index, details,
795                                      p->value, slow);
796        Return(p->value);
797      }
798    }
799  }
800
801  Bind(&dictionary_properties);
802  {
803    Comment("dictionary property store");
804    // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
805    // seeing global objects here (which would need special handling).
806
807    Variable var_name_index(this, MachineType::PointerRepresentation());
808    Label dictionary_found(this, &var_name_index), not_found(this);
809    NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
810                                         &var_name_index, &not_found);
811    Bind(&dictionary_found);
812    {
813      Label overwrite(this);
814      Node* details = LoadDetailsByKeyIndex<NameDictionary>(
815          properties, var_name_index.value());
816      JumpIfDataProperty(details, &overwrite, &readonly);
817
818      // Accessor case.
819      var_accessor_pair.Bind(LoadValueByKeyIndex<NameDictionary>(
820          properties, var_name_index.value()));
821      var_accessor_holder.Bind(receiver);
822      Goto(&accessor);
823
824      Bind(&overwrite);
825      {
826        StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(),
827                                             p->value);
828        Return(p->value);
829      }
830    }
831
832    Bind(&not_found);
833    {
834      LookupPropertyOnPrototypeChain(receiver_map, p->name, &accessor,
835                                     &var_accessor_pair, &var_accessor_holder,
836                                     &readonly, slow);
837      Add<NameDictionary>(properties, p->name, p->value, slow);
838      Return(p->value);
839    }
840  }
841
842  Bind(&accessor);
843  {
844    Label not_callable(this);
845    Node* accessor_pair = var_accessor_pair.value();
846    GotoIf(IsAccessorInfoMap(LoadMap(accessor_pair)), slow);
847    CSA_ASSERT(this, HasInstanceType(accessor_pair, ACCESSOR_PAIR_TYPE));
848    Node* setter = LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
849    Node* setter_map = LoadMap(setter);
850    // FunctionTemplateInfo setters are not supported yet.
851    GotoIf(IsFunctionTemplateInfoMap(setter_map), slow);
852    GotoIfNot(IsCallableMap(setter_map), &not_callable);
853
854    Callable callable = CodeFactory::Call(isolate());
855    CallJS(callable, p->context, setter, receiver, p->value);
856    Return(p->value);
857
858    Bind(&not_callable);
859    {
860      if (language_mode == STRICT) {
861        Node* message =
862            SmiConstant(Smi::FromInt(MessageTemplate::kNoSetterInCallback));
863        TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
864                        var_accessor_holder.value());
865      } else {
866        DCHECK_EQ(SLOPPY, language_mode);
867        Return(p->value);
868      }
869    }
870  }
871
872  Bind(&readonly);
873  {
874    if (language_mode == STRICT) {
875      Node* message =
876          SmiConstant(Smi::FromInt(MessageTemplate::kStrictReadOnlyProperty));
877      Node* type = Typeof(p->receiver, p->context);
878      TailCallRuntime(Runtime::kThrowTypeError, p->context, message, p->name,
879                      type, p->receiver);
880    } else {
881      DCHECK_EQ(SLOPPY, language_mode);
882      Return(p->value);
883    }
884  }
885
886  Bind(&stub_cache);
887  {
888    Comment("stub cache probe");
889    Variable var_handler(this, MachineRepresentation::kTagged);
890    Label found_handler(this, &var_handler), stub_cache_miss(this);
891    TryProbeStubCache(isolate()->store_stub_cache(), receiver, p->name,
892                      &found_handler, &var_handler, &stub_cache_miss);
893    Bind(&found_handler);
894    {
895      Comment("KeyedStoreGeneric found handler");
896      HandleStoreICHandlerCase(p, var_handler.value(), &stub_cache_miss);
897    }
898    Bind(&stub_cache_miss);
899    {
900      Comment("KeyedStoreGeneric_miss");
901      TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value,
902                      p->slot, p->vector, p->receiver, p->name);
903    }
904  }
905}
906
907void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
908  typedef StoreWithVectorDescriptor Descriptor;
909
910  Node* receiver = Parameter(Descriptor::kReceiver);
911  Node* name = Parameter(Descriptor::kName);
912  Node* value = Parameter(Descriptor::kValue);
913  Node* slot = Parameter(Descriptor::kSlot);
914  Node* vector = Parameter(Descriptor::kVector);
915  Node* context = Parameter(Descriptor::kContext);
916
917  Variable var_index(this, MachineType::PointerRepresentation());
918  Variable var_unique(this, MachineRepresentation::kTagged);
919  var_unique.Bind(name);  // Dummy initialization.
920  Label if_index(this), if_unique_name(this), slow(this);
921
922  GotoIf(TaggedIsSmi(receiver), &slow);
923  Node* receiver_map = LoadMap(receiver);
924  Node* instance_type = LoadMapInstanceType(receiver_map);
925  // Receivers requiring non-standard element accesses (interceptors, access
926  // checks, strings and string wrappers, proxies) are handled in the runtime.
927  GotoIf(Int32LessThanOrEqual(instance_type,
928                              Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
929         &slow);
930
931  TryToName(name, &if_index, &var_index, &if_unique_name, &var_unique, &slow);
932
933  Bind(&if_index);
934  {
935    Comment("integer index");
936    EmitGenericElementStore(receiver, receiver_map, instance_type,
937                            var_index.value(), value, context, &slow);
938  }
939
940  Bind(&if_unique_name);
941  {
942    Comment("key is unique name");
943    StoreICParameters p(context, receiver, var_unique.value(), value, slot,
944                        vector);
945    EmitGenericPropertyStore(receiver, receiver_map, &p, &slow, language_mode);
946  }
947
948  Bind(&slow);
949  {
950    Comment("KeyedStoreGeneric_slow");
951    TailCallRuntime(Runtime::kSetProperty, context, receiver, name, value,
952                    SmiConstant(language_mode));
953  }
954}
955
956}  // namespace internal
957}  // namespace v8
958