• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 namespace v8 {
16 namespace internal {
17 
18 using compiler::Node;
19 
20 class KeyedStoreGenericAssembler : public AccessorAssembler {
21  public:
KeyedStoreGenericAssembler(compiler::CodeAssemblerState * state)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 
Generate(compiler::CodeAssemblerState * state,LanguageMode language_mode)85 void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state,
86                                           LanguageMode language_mode) {
87   KeyedStoreGenericAssembler assembler(state);
88   assembler.KeyedStoreGeneric(language_mode);
89 }
90 
BranchIfPrototypesHaveNonFastElements(Node * receiver_map,Label * non_fast_elements,Label * only_fast_elements)91 void 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 
TryRewriteElements(Node * receiver,Node * receiver_map,Node * elements,Node * native_context,ElementsKind from_kind,ElementsKind to_kind,Label * bailout)119 void 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 
TryChangeToHoleyMapHelper(Node * receiver,Node * receiver_map,Node * native_context,ElementsKind packed_kind,ElementsKind holey_kind,Label * done,Label * map_mismatch,Label * bailout)164 void 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 
TryChangeToHoleyMap(Node * receiver,Node * receiver_map,Node * current_elements_kind,Node * context,ElementsKind packed_kind,Label * bailout)181 void 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 
TryChangeToHoleyMapMulti(Node * receiver,Node * receiver_map,Node * current_elements_kind,Node * context,ElementsKind packed_kind,ElementsKind packed_kind_2,Label * bailout)195 void 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 
MaybeUpdateLengthAndReturn(Node * receiver,Node * index,Node * value,UpdateLength update_length)219 void 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 
StoreElementWithCapacity(Node * receiver,Node * receiver_map,Node * elements,Node * elements_kind,Node * intptr_index,Node * value,Node * context,Label * slow,UpdateLength update_length)229 void 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 
EmitGenericElementStore(Node * receiver,Node * receiver_map,Node * instance_type,Node * intptr_index,Node * value,Node * context,Label * slow)422 void 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 
JumpIfDataProperty(Node * details,Label * writable,Label * readonly)509 void 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 
LookupPropertyOnPrototypeChain(Node * receiver_map,Node * name,Label * accessor,Variable * var_accessor_pair,Variable * var_accessor_holder,Label * readonly,Label * bailout)520 void 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 
CheckFieldType(Node * descriptors,Node * name_index,Node * representation,Node * value,Label * bailout)611 void 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 
OverwriteExistingFastProperty(Node * object,Node * object_map,Node * properties,Node * descriptors,Node * descriptor_name_index,Node * details,Node * value,Label * slow)669 void 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 
EmitGenericPropertyStore(Node * receiver,Node * receiver_map,const StoreICParameters * p,Label * slow,LanguageMode language_mode)751 void 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 
KeyedStoreGeneric(LanguageMode language_mode)907 void 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