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/codegen/code-factory.h"
8 #include "src/codegen/code-stub-assembler.h"
9 #include "src/codegen/interface-descriptors.h"
10 #include "src/execution/isolate.h"
11 #include "src/ic/accessor-assembler.h"
12 #include "src/objects/contexts.h"
13 #include "src/objects/feedback-vector.h"
14 #include "src/objects/objects-inl.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 enum class StoreMode { kOrdinary, kInLiteral };
20 
21 class KeyedStoreGenericAssembler : public AccessorAssembler {
22  public:
KeyedStoreGenericAssembler(compiler::CodeAssemblerState * state,StoreMode mode)23   explicit KeyedStoreGenericAssembler(compiler::CodeAssemblerState* state,
24                                       StoreMode mode)
25       : AccessorAssembler(state), mode_(mode) {}
26 
27   void KeyedStoreGeneric();
28 
29   void StoreIC_NoFeedback();
30 
31   // Generates code for [[Set]] operation, the |unique_name| is supposed to be
32   // unique otherwise this code will always go to runtime.
33   void SetProperty(TNode<Context> context, TNode<JSReceiver> receiver,
34                    TNode<BoolT> is_simple_receiver, TNode<Name> unique_name,
35                    TNode<Object> value, LanguageMode language_mode);
36 
37   // [[Set]], but more generic than the above. This impl does essentially the
38   // same as "KeyedStoreGeneric" but does not use feedback slot and uses a
39   // hardcoded LanguageMode instead of trying to deduce it from the feedback
40   // slot's kind.
41   void SetProperty(TNode<Context> context, TNode<Object> receiver,
42                    TNode<Object> key, TNode<Object> value,
43                    LanguageMode language_mode);
44 
45  private:
46   StoreMode mode_;
47 
48   enum UpdateLength {
49     kDontChangeLength,
50     kIncrementLengthByOne,
51     kBumpLengthWithGap
52   };
53 
54   enum UseStubCache { kUseStubCache, kDontUseStubCache };
55 
56   // Helper that is used by the public KeyedStoreGeneric and by SetProperty.
57   void KeyedStoreGeneric(TNode<Context> context, TNode<Object> receiver,
58                          TNode<Object> key, TNode<Object> value,
59                          Maybe<LanguageMode> language_mode);
60 
61   void EmitGenericElementStore(TNode<JSObject> receiver,
62                                TNode<Map> receiver_map,
63                                TNode<Uint16T> instance_type,
64                                TNode<IntPtrT> index, TNode<Object> value,
65                                TNode<Context> context, Label* slow);
66 
67   // If language mode is not provided it is deduced from the feedback slot's
68   // kind.
69   void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
70                                 TNode<Map> receiver_map,
71                                 const StoreICParameters* p,
72                                 ExitPoint* exit_point, Label* slow,
73                                 Maybe<LanguageMode> maybe_language_mode);
74 
EmitGenericPropertyStore(TNode<JSReceiver> receiver,TNode<Map> receiver_map,const StoreICParameters * p,Label * slow)75   void EmitGenericPropertyStore(TNode<JSReceiver> receiver,
76                                 TNode<Map> receiver_map,
77                                 const StoreICParameters* p, Label* slow) {
78     ExitPoint direct_exit(this);
79     EmitGenericPropertyStore(receiver, receiver_map, p, &direct_exit, slow,
80                              Nothing<LanguageMode>());
81   }
82 
83   void BranchIfPrototypesMayHaveReadOnlyElements(
84       TNode<Map> receiver_map, Label* maybe_read_only_elements,
85       Label* only_fast_writable_elements);
86 
87   void TryRewriteElements(TNode<JSObject> receiver, TNode<Map> receiver_map,
88                           TNode<FixedArrayBase> elements,
89                           TNode<NativeContext> native_context,
90                           ElementsKind from_kind, ElementsKind to_kind,
91                           Label* bailout);
92 
93   void StoreElementWithCapacity(TNode<JSObject> receiver,
94                                 TNode<Map> receiver_map,
95                                 TNode<FixedArrayBase> elements,
96                                 TNode<Word32T> elements_kind,
97                                 TNode<IntPtrT> index, TNode<Object> value,
98                                 TNode<Context> context, Label* slow,
99                                 UpdateLength update_length);
100 
101   void MaybeUpdateLengthAndReturn(TNode<JSObject> receiver,
102                                   TNode<IntPtrT> index, TNode<Object> value,
103                                   UpdateLength update_length);
104 
105   void TryChangeToHoleyMapHelper(TNode<JSObject> receiver,
106                                  TNode<Map> receiver_map,
107                                  TNode<NativeContext> native_context,
108                                  ElementsKind packed_kind,
109                                  ElementsKind holey_kind, Label* done,
110                                  Label* map_mismatch, Label* bailout);
111   void TryChangeToHoleyMap(TNode<JSObject> receiver, TNode<Map> receiver_map,
112                            TNode<Word32T> current_elements_kind,
113                            TNode<Context> context, ElementsKind packed_kind,
114                            Label* bailout);
115   void TryChangeToHoleyMapMulti(TNode<JSObject> receiver,
116                                 TNode<Map> receiver_map,
117                                 TNode<Word32T> current_elements_kind,
118                                 TNode<Context> context,
119                                 ElementsKind packed_kind,
120                                 ElementsKind packed_kind_2, Label* bailout);
121 
122   void LookupPropertyOnPrototypeChain(
123       TNode<Map> receiver_map, TNode<Name> name, Label* accessor,
124       TVariable<Object>* var_accessor_pair,
125       TVariable<HeapObject>* var_accessor_holder, Label* readonly,
126       Label* bailout);
127 
128   TNode<Map> FindCandidateStoreICTransitionMapHandler(TNode<Map> map,
129                                                       TNode<Name> name,
130                                                       Label* slow);
131 
IsKeyedStore() const132   bool IsKeyedStore() const { return mode_ == StoreMode::kOrdinary; }
IsStoreInLiteral() const133   bool IsStoreInLiteral() const { return mode_ == StoreMode::kInLiteral; }
134 
ShouldCheckPrototype() const135   bool ShouldCheckPrototype() const { return IsKeyedStore(); }
ShouldReconfigureExisting() const136   bool ShouldReconfigureExisting() const { return IsStoreInLiteral(); }
ShouldCallSetter() const137   bool ShouldCallSetter() const { return IsKeyedStore(); }
ShouldCheckPrototypeValidity() const138   bool ShouldCheckPrototypeValidity() const {
139     // We don't do this for "in-literal" stores, because it is impossible for
140     // the target object to be a "prototype"
141     return !IsStoreInLiteral();
142   }
143 };
144 
Generate(compiler::CodeAssemblerState * state)145 void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state) {
146   KeyedStoreGenericAssembler assembler(state, StoreMode::kOrdinary);
147   assembler.KeyedStoreGeneric();
148 }
149 
Generate(compiler::CodeAssemblerState * state)150 void StoreICNoFeedbackGenerator::Generate(compiler::CodeAssemblerState* state) {
151   KeyedStoreGenericAssembler assembler(state, StoreMode::kOrdinary);
152   assembler.StoreIC_NoFeedback();
153 }
154 
SetProperty(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<JSReceiver> receiver,TNode<BoolT> is_simple_receiver,TNode<Name> name,TNode<Object> value,LanguageMode language_mode)155 void KeyedStoreGenericGenerator::SetProperty(
156     compiler::CodeAssemblerState* state, TNode<Context> context,
157     TNode<JSReceiver> receiver, TNode<BoolT> is_simple_receiver,
158     TNode<Name> name, TNode<Object> value, LanguageMode language_mode) {
159   KeyedStoreGenericAssembler assembler(state, StoreMode::kOrdinary);
160   assembler.SetProperty(context, receiver, is_simple_receiver, name, value,
161                         language_mode);
162 }
163 
SetProperty(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<Object> receiver,TNode<Object> key,TNode<Object> value,LanguageMode language_mode)164 void KeyedStoreGenericGenerator::SetProperty(
165     compiler::CodeAssemblerState* state, TNode<Context> context,
166     TNode<Object> receiver, TNode<Object> key, TNode<Object> value,
167     LanguageMode language_mode) {
168   KeyedStoreGenericAssembler assembler(state, StoreMode::kOrdinary);
169   assembler.SetProperty(context, receiver, key, value, language_mode);
170 }
171 
SetPropertyInLiteral(compiler::CodeAssemblerState * state,TNode<Context> context,TNode<JSObject> receiver,TNode<Object> key,TNode<Object> value)172 void KeyedStoreGenericGenerator::SetPropertyInLiteral(
173     compiler::CodeAssemblerState* state, TNode<Context> context,
174     TNode<JSObject> receiver, TNode<Object> key, TNode<Object> value) {
175   KeyedStoreGenericAssembler assembler(state, StoreMode::kInLiteral);
176   assembler.SetProperty(context, receiver, key, value, LanguageMode::kStrict);
177 }
178 
BranchIfPrototypesMayHaveReadOnlyElements(TNode<Map> receiver_map,Label * maybe_read_only_elements,Label * only_fast_writable_elements)179 void KeyedStoreGenericAssembler::BranchIfPrototypesMayHaveReadOnlyElements(
180     TNode<Map> receiver_map, Label* maybe_read_only_elements,
181     Label* only_fast_writable_elements) {
182   TVARIABLE(Map, var_map);
183   var_map = receiver_map;
184   Label loop_body(this, &var_map);
185   Goto(&loop_body);
186 
187   BIND(&loop_body);
188   {
189     TNode<Map> map = var_map.value();
190     TNode<HeapObject> prototype = LoadMapPrototype(map);
191     GotoIf(IsNull(prototype), only_fast_writable_elements);
192     TNode<Map> prototype_map = LoadMap(prototype);
193     var_map = prototype_map;
194     TNode<Uint16T> instance_type = LoadMapInstanceType(prototype_map);
195     GotoIf(IsCustomElementsReceiverInstanceType(instance_type),
196            maybe_read_only_elements);
197     TNode<Int32T> elements_kind = LoadMapElementsKind(prototype_map);
198     GotoIf(IsFastOrNonExtensibleOrSealedElementsKind(elements_kind),
199            &loop_body);
200     GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
201     Goto(maybe_read_only_elements);
202   }
203 }
204 
TryRewriteElements(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<FixedArrayBase> elements,TNode<NativeContext> native_context,ElementsKind from_kind,ElementsKind to_kind,Label * bailout)205 void KeyedStoreGenericAssembler::TryRewriteElements(
206     TNode<JSObject> receiver, TNode<Map> receiver_map,
207     TNode<FixedArrayBase> elements, TNode<NativeContext> native_context,
208     ElementsKind from_kind, ElementsKind to_kind, Label* bailout) {
209   DCHECK(IsFastPackedElementsKind(from_kind));
210   ElementsKind holey_from_kind = GetHoleyElementsKind(from_kind);
211   ElementsKind holey_to_kind = GetHoleyElementsKind(to_kind);
212   if (AllocationSite::ShouldTrack(from_kind, to_kind)) {
213     TrapAllocationMemento(receiver, bailout);
214   }
215   Label perform_transition(this), check_holey_map(this);
216   TVARIABLE(Map, var_target_map);
217   // Check if the receiver has the default |from_kind| map.
218   {
219     TNode<Map> packed_map = LoadJSArrayElementsMap(from_kind, native_context);
220     GotoIf(TaggedNotEqual(receiver_map, packed_map), &check_holey_map);
221     var_target_map = CAST(
222         LoadContextElement(native_context, Context::ArrayMapIndex(to_kind)));
223     Goto(&perform_transition);
224   }
225 
226   // Check if the receiver has the default |holey_from_kind| map.
227   BIND(&check_holey_map);
228   {
229     TNode<Object> holey_map = LoadContextElement(
230         native_context, Context::ArrayMapIndex(holey_from_kind));
231     GotoIf(TaggedNotEqual(receiver_map, holey_map), bailout);
232     var_target_map = CAST(LoadContextElement(
233         native_context, Context::ArrayMapIndex(holey_to_kind)));
234     Goto(&perform_transition);
235   }
236 
237   // Found a supported transition target map, perform the transition!
238   BIND(&perform_transition);
239   {
240     if (IsDoubleElementsKind(from_kind) != IsDoubleElementsKind(to_kind)) {
241       TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
242       GrowElementsCapacity(receiver, elements, from_kind, to_kind, capacity,
243                            capacity, bailout);
244     }
245     StoreMap(receiver, var_target_map.value());
246   }
247 }
248 
TryChangeToHoleyMapHelper(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<NativeContext> native_context,ElementsKind packed_kind,ElementsKind holey_kind,Label * done,Label * map_mismatch,Label * bailout)249 void KeyedStoreGenericAssembler::TryChangeToHoleyMapHelper(
250     TNode<JSObject> receiver, TNode<Map> receiver_map,
251     TNode<NativeContext> native_context, ElementsKind packed_kind,
252     ElementsKind holey_kind, Label* done, Label* map_mismatch, Label* bailout) {
253   TNode<Map> packed_map = LoadJSArrayElementsMap(packed_kind, native_context);
254   GotoIf(TaggedNotEqual(receiver_map, packed_map), map_mismatch);
255   if (AllocationSite::ShouldTrack(packed_kind, holey_kind)) {
256     TrapAllocationMemento(receiver, bailout);
257   }
258   TNode<Map> holey_map = CAST(
259       LoadContextElement(native_context, Context::ArrayMapIndex(holey_kind)));
260   StoreMap(receiver, holey_map);
261   Goto(done);
262 }
263 
TryChangeToHoleyMap(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Word32T> current_elements_kind,TNode<Context> context,ElementsKind packed_kind,Label * bailout)264 void KeyedStoreGenericAssembler::TryChangeToHoleyMap(
265     TNode<JSObject> receiver, TNode<Map> receiver_map,
266     TNode<Word32T> current_elements_kind, TNode<Context> context,
267     ElementsKind packed_kind, Label* bailout) {
268   ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
269   Label already_holey(this);
270 
271   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
272          &already_holey);
273   TNode<NativeContext> native_context = LoadNativeContext(context);
274   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
275                             holey_kind, &already_holey, bailout, bailout);
276   BIND(&already_holey);
277 }
278 
TryChangeToHoleyMapMulti(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Word32T> current_elements_kind,TNode<Context> context,ElementsKind packed_kind,ElementsKind packed_kind_2,Label * bailout)279 void KeyedStoreGenericAssembler::TryChangeToHoleyMapMulti(
280     TNode<JSObject> receiver, TNode<Map> receiver_map,
281     TNode<Word32T> current_elements_kind, TNode<Context> context,
282     ElementsKind packed_kind, ElementsKind packed_kind_2, Label* bailout) {
283   ElementsKind holey_kind = GetHoleyElementsKind(packed_kind);
284   ElementsKind holey_kind_2 = GetHoleyElementsKind(packed_kind_2);
285   Label already_holey(this), check_other_kind(this);
286 
287   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind)),
288          &already_holey);
289   GotoIf(Word32Equal(current_elements_kind, Int32Constant(holey_kind_2)),
290          &already_holey);
291 
292   TNode<NativeContext> native_context = LoadNativeContext(context);
293   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context, packed_kind,
294                             holey_kind, &already_holey, &check_other_kind,
295                             bailout);
296   BIND(&check_other_kind);
297   TryChangeToHoleyMapHelper(receiver, receiver_map, native_context,
298                             packed_kind_2, holey_kind_2, &already_holey,
299                             bailout, bailout);
300   BIND(&already_holey);
301 }
302 
MaybeUpdateLengthAndReturn(TNode<JSObject> receiver,TNode<IntPtrT> index,TNode<Object> value,UpdateLength update_length)303 void KeyedStoreGenericAssembler::MaybeUpdateLengthAndReturn(
304     TNode<JSObject> receiver, TNode<IntPtrT> index, TNode<Object> value,
305     UpdateLength update_length) {
306   if (update_length != kDontChangeLength) {
307     TNode<Smi> new_length = SmiTag(Signed(IntPtrAdd(index, IntPtrConstant(1))));
308     StoreObjectFieldNoWriteBarrier(receiver, JSArray::kLengthOffset,
309                                    new_length);
310   }
311   Return(value);
312 }
313 
StoreElementWithCapacity(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<FixedArrayBase> elements,TNode<Word32T> elements_kind,TNode<IntPtrT> index,TNode<Object> value,TNode<Context> context,Label * slow,UpdateLength update_length)314 void KeyedStoreGenericAssembler::StoreElementWithCapacity(
315     TNode<JSObject> receiver, TNode<Map> receiver_map,
316     TNode<FixedArrayBase> elements, TNode<Word32T> elements_kind,
317     TNode<IntPtrT> index, TNode<Object> value, TNode<Context> context,
318     Label* slow, UpdateLength update_length) {
319   if (update_length != kDontChangeLength) {
320     CSA_DCHECK(this, IsJSArrayMap(receiver_map));
321     // Check if the length property is writable. The fast check is only
322     // supported for fast properties.
323     GotoIf(IsDictionaryMap(receiver_map), slow);
324     // The length property is non-configurable, so it's guaranteed to always
325     // be the first property.
326     TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
327     TNode<Uint32T> details = LoadDetailsByDescriptorEntry(descriptors, 0);
328     GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
329            slow);
330   }
331   STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize);
332   const int kHeaderSize = FixedArray::kHeaderSize - kHeapObjectTag;
333 
334   Label check_double_elements(this), check_cow_elements(this);
335   TNode<Map> elements_map = LoadMap(elements);
336   GotoIf(IsNotFixedArrayMap(elements_map), &check_double_elements);
337 
338   // FixedArray backing store -> Smi or object elements.
339   {
340     TNode<IntPtrT> offset =
341         ElementOffsetFromIndex(index, PACKED_ELEMENTS, kHeaderSize);
342     if (!IsStoreInLiteral()) {
343       // Check if we're about to overwrite the hole. We can safely do that
344       // only if there can be no setters on the prototype chain.
345       // If we know that we're storing beyond the previous array length, we
346       // can skip the hole check (and always assume the hole).
347       {
348         Label hole_check_passed(this);
349         if (update_length == kDontChangeLength) {
350           TNode<Object> element =
351               CAST(Load(MachineType::AnyTagged(), elements, offset));
352           GotoIf(IsNotTheHole(element), &hole_check_passed);
353         }
354         BranchIfPrototypesMayHaveReadOnlyElements(receiver_map, slow,
355                                                   &hole_check_passed);
356         BIND(&hole_check_passed);
357       }
358     }
359 
360     // Check if the value we're storing matches the elements_kind. Smis
361     // can always be stored.
362     {
363       Label non_smi_value(this);
364       GotoIfNot(TaggedIsSmi(value), &non_smi_value);
365       // If we're about to introduce holes, ensure holey elements.
366       if (update_length == kBumpLengthWithGap) {
367         TryChangeToHoleyMapMulti(receiver, receiver_map, elements_kind, context,
368                                  PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, slow);
369       }
370       StoreNoWriteBarrier(MachineRepresentation::kTaggedSigned, elements,
371                           offset, value);
372       MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
373 
374       BIND(&non_smi_value);
375     }
376 
377     // Check if we already have object elements; just do the store if so.
378     {
379       Label must_transition(this);
380       STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
381       STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
382       GotoIf(Int32LessThanOrEqual(elements_kind,
383                                   Int32Constant(HOLEY_SMI_ELEMENTS)),
384              &must_transition);
385       if (update_length == kBumpLengthWithGap) {
386         TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
387                             PACKED_ELEMENTS, slow);
388       }
389       Store(elements, offset, value);
390       MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
391 
392       BIND(&must_transition);
393     }
394 
395     // Transition to the required ElementsKind.
396     {
397       Label transition_to_double(this), transition_to_object(this);
398       TNode<NativeContext> native_context = LoadNativeContext(context);
399       Branch(IsHeapNumber(CAST(value)), &transition_to_double,
400              &transition_to_object);
401       BIND(&transition_to_double);
402       {
403         // If we're adding holes at the end, always transition to a holey
404         // elements kind, otherwise try to remain packed.
405         ElementsKind target_kind = update_length == kBumpLengthWithGap
406                                        ? HOLEY_DOUBLE_ELEMENTS
407                                        : PACKED_DOUBLE_ELEMENTS;
408         TryRewriteElements(receiver, receiver_map, elements, native_context,
409                            PACKED_SMI_ELEMENTS, target_kind, slow);
410         // Reload migrated elements.
411         TNode<FixedArrayBase> double_elements = LoadElements(receiver);
412         TNode<IntPtrT> double_offset =
413             ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, kHeaderSize);
414         // Make sure we do not store signalling NaNs into double arrays.
415         TNode<Float64T> double_value =
416             Float64SilenceNaN(LoadHeapNumberValue(CAST(value)));
417         StoreNoWriteBarrier(MachineRepresentation::kFloat64, double_elements,
418                             double_offset, double_value);
419         MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
420       }
421 
422       BIND(&transition_to_object);
423       {
424         // If we're adding holes at the end, always transition to a holey
425         // elements kind, otherwise try to remain packed.
426         ElementsKind target_kind = update_length == kBumpLengthWithGap
427                                        ? HOLEY_ELEMENTS
428                                        : PACKED_ELEMENTS;
429         TryRewriteElements(receiver, receiver_map, elements, native_context,
430                            PACKED_SMI_ELEMENTS, target_kind, slow);
431         // The elements backing store didn't change, no reload necessary.
432         CSA_DCHECK(this, TaggedEqual(elements, LoadElements(receiver)));
433         Store(elements, offset, value);
434         MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
435       }
436     }
437   }
438 
439   BIND(&check_double_elements);
440   GotoIf(IsNotFixedDoubleArrayMap(elements_map), &check_cow_elements);
441   // FixedDoubleArray backing store -> double elements.
442   {
443     TNode<IntPtrT> offset =
444         ElementOffsetFromIndex(index, PACKED_DOUBLE_ELEMENTS, kHeaderSize);
445     if (!IsStoreInLiteral()) {
446       // Check if we're about to overwrite the hole. We can safely do that
447       // only if there can be no setters on the prototype chain.
448       {
449         Label hole_check_passed(this);
450         // If we know that we're storing beyond the previous array length, we
451         // can skip the hole check (and always assume the hole).
452         if (update_length == kDontChangeLength) {
453           Label found_hole(this);
454           LoadDoubleWithHoleCheck(elements, offset, &found_hole,
455                                   MachineType::None());
456           Goto(&hole_check_passed);
457           BIND(&found_hole);
458         }
459         BranchIfPrototypesMayHaveReadOnlyElements(receiver_map, slow,
460                                                   &hole_check_passed);
461         BIND(&hole_check_passed);
462       }
463     }
464 
465     // Try to store the value as a double.
466     {
467       Label non_number_value(this);
468       TNode<Float64T> double_value =
469           TryTaggedToFloat64(value, &non_number_value);
470 
471       // Make sure we do not store signalling NaNs into double arrays.
472       double_value = Float64SilenceNaN(double_value);
473       // If we're about to introduce holes, ensure holey elements.
474       if (update_length == kBumpLengthWithGap) {
475         TryChangeToHoleyMap(receiver, receiver_map, elements_kind, context,
476                             PACKED_DOUBLE_ELEMENTS, slow);
477       }
478       StoreNoWriteBarrier(MachineRepresentation::kFloat64, elements, offset,
479                           double_value);
480       MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
481 
482       BIND(&non_number_value);
483     }
484 
485     // Transition to object elements.
486     {
487       TNode<NativeContext> native_context = LoadNativeContext(context);
488       ElementsKind target_kind = update_length == kBumpLengthWithGap
489                                      ? HOLEY_ELEMENTS
490                                      : PACKED_ELEMENTS;
491       TryRewriteElements(receiver, receiver_map, elements, native_context,
492                          PACKED_DOUBLE_ELEMENTS, target_kind, slow);
493       // Reload migrated elements.
494       TNode<FixedArrayBase> fast_elements = LoadElements(receiver);
495       TNode<IntPtrT> fast_offset =
496           ElementOffsetFromIndex(index, PACKED_ELEMENTS, kHeaderSize);
497       Store(fast_elements, fast_offset, value);
498       MaybeUpdateLengthAndReturn(receiver, index, value, update_length);
499     }
500   }
501 
502   BIND(&check_cow_elements);
503   {
504     // TODO(jkummerow): Use GrowElementsCapacity instead of bailing out.
505     Goto(slow);
506   }
507 }
508 
EmitGenericElementStore(TNode<JSObject> receiver,TNode<Map> receiver_map,TNode<Uint16T> instance_type,TNode<IntPtrT> index,TNode<Object> value,TNode<Context> context,Label * slow)509 void KeyedStoreGenericAssembler::EmitGenericElementStore(
510     TNode<JSObject> receiver, TNode<Map> receiver_map,
511     TNode<Uint16T> instance_type, TNode<IntPtrT> index, TNode<Object> value,
512     TNode<Context> context, Label* slow) {
513   Label if_fast(this), if_in_bounds(this), if_increment_length_by_one(this),
514       if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
515       if_typed_array(this), if_dictionary(this);
516   TNode<FixedArrayBase> elements = LoadElements(receiver);
517   TNode<Int32T> elements_kind = LoadMapElementsKind(receiver_map);
518   Branch(IsFastElementsKind(elements_kind), &if_fast, &if_nonfast);
519   BIND(&if_fast);
520 
521   Label if_array(this);
522   GotoIf(IsJSArrayInstanceType(instance_type), &if_array);
523   {
524     TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
525     Branch(UintPtrLessThan(index, capacity), &if_in_bounds, &if_grow);
526   }
527   BIND(&if_array);
528   {
529     TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(CAST(receiver)));
530     GotoIf(UintPtrLessThan(index, length), &if_in_bounds);
531     TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements));
532     GotoIf(UintPtrGreaterThanOrEqual(index, capacity), &if_grow);
533     Branch(WordEqual(index, length), &if_increment_length_by_one,
534            &if_bump_length_with_gap);
535   }
536 
537   BIND(&if_in_bounds);
538   {
539     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
540                              index, value, context, slow, kDontChangeLength);
541   }
542 
543   BIND(&if_increment_length_by_one);
544   {
545     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
546                              index, value, context, slow,
547                              kIncrementLengthByOne);
548   }
549 
550   BIND(&if_bump_length_with_gap);
551   {
552     StoreElementWithCapacity(receiver, receiver_map, elements, elements_kind,
553                              index, value, context, slow, kBumpLengthWithGap);
554   }
555 
556   // Out-of-capacity accesses (index >= capacity) jump here. Additionally,
557   // an ElementsKind transition might be necessary.
558   // The index can also be negative or larger than kMaxElementIndex at this
559   // point! Jump to the runtime in that case to convert it to a named property.
560   BIND(&if_grow);
561   {
562     Comment("Grow backing store");
563     // TODO(jkummerow): Support inline backing store growth.
564     Goto(slow);
565   }
566 
567   // Any ElementsKind > LAST_FAST_ELEMENTS_KIND jumps here for further
568   // dispatch.
569   BIND(&if_nonfast);
570   {
571     STATIC_ASSERT(LAST_ELEMENTS_KIND ==
572                   LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
573     GotoIf(Int32GreaterThanOrEqual(
574                elements_kind,
575                Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
576            &if_typed_array);
577     GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
578            &if_dictionary);
579     Goto(slow);
580   }
581 
582   BIND(&if_dictionary);
583   {
584     Comment("Dictionary");
585     // TODO(jkummerow): Support storing to dictionary elements.
586     Goto(slow);
587   }
588 
589   BIND(&if_typed_array);
590   {
591     Comment("Typed array");
592     // TODO(jkummerow): Support typed arrays. Note: RAB / GSAB backed typed
593     // arrays end up here too.
594     Goto(slow);
595   }
596 }
597 
LookupPropertyOnPrototypeChain(TNode<Map> receiver_map,TNode<Name> name,Label * accessor,TVariable<Object> * var_accessor_pair,TVariable<HeapObject> * var_accessor_holder,Label * readonly,Label * bailout)598 void KeyedStoreGenericAssembler::LookupPropertyOnPrototypeChain(
599     TNode<Map> receiver_map, TNode<Name> name, Label* accessor,
600     TVariable<Object>* var_accessor_pair,
601     TVariable<HeapObject>* var_accessor_holder, Label* readonly,
602     Label* bailout) {
603   Label ok_to_write(this);
604   TVARIABLE(HeapObject, var_holder);
605   TVARIABLE(Map, var_holder_map);
606   var_holder = LoadMapPrototype(receiver_map);
607   var_holder_map = LoadMap(var_holder.value());
608 
609   Label loop(this, {&var_holder, &var_holder_map});
610   Goto(&loop);
611   BIND(&loop);
612   {
613     TNode<HeapObject> holder = var_holder.value();
614     GotoIf(IsNull(holder), &ok_to_write);
615     TNode<Map> holder_map = var_holder_map.value();
616     TNode<Uint16T> instance_type = LoadMapInstanceType(holder_map);
617     Label next_proto(this);
618     {
619       Label found(this), found_fast(this), found_dict(this), found_global(this);
620       TVARIABLE(HeapObject, var_meta_storage);
621       TVARIABLE(IntPtrT, var_entry);
622       TryLookupProperty(holder, holder_map, instance_type, name, &found_fast,
623                         &found_dict, &found_global, &var_meta_storage,
624                         &var_entry, &next_proto, bailout);
625       BIND(&found_fast);
626       {
627         TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value());
628         TNode<IntPtrT> name_index = var_entry.value();
629         TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index);
630         JumpIfDataProperty(details, &ok_to_write, readonly);
631 
632         // Accessor case.
633         // TODO(jkummerow): Implement a trimmed-down
634         // LoadAccessorFromFastObject.
635         LoadPropertyFromFastObject(holder, holder_map, descriptors, name_index,
636                                    details, var_accessor_pair);
637         *var_accessor_holder = holder;
638         Goto(accessor);
639       }
640 
641       BIND(&found_dict);
642       {
643         TNode<PropertyDictionary> dictionary = CAST(var_meta_storage.value());
644         TNode<IntPtrT> entry = var_entry.value();
645         TNode<Uint32T> details = LoadDetailsByKeyIndex(dictionary, entry);
646         JumpIfDataProperty(details, &ok_to_write, readonly);
647 
648         if (accessor != nullptr) {
649           // Accessor case.
650           *var_accessor_pair = LoadValueByKeyIndex(dictionary, entry);
651           *var_accessor_holder = holder;
652           Goto(accessor);
653         } else {
654           Goto(&ok_to_write);
655         }
656       }
657 
658       BIND(&found_global);
659       {
660         TNode<GlobalDictionary> dictionary = CAST(var_meta_storage.value());
661         TNode<IntPtrT> entry = var_entry.value();
662         TNode<PropertyCell> property_cell =
663             CAST(LoadValueByKeyIndex(dictionary, entry));
664         TNode<Object> value =
665             LoadObjectField(property_cell, PropertyCell::kValueOffset);
666         GotoIf(TaggedEqual(value, TheHoleConstant()), &next_proto);
667         TNode<Uint32T> details = Unsigned(LoadAndUntagToWord32ObjectField(
668             property_cell, PropertyCell::kPropertyDetailsRawOffset));
669         JumpIfDataProperty(details, &ok_to_write, readonly);
670 
671         if (accessor != nullptr) {
672           // Accessor case.
673           *var_accessor_pair = value;
674           *var_accessor_holder = holder;
675           Goto(accessor);
676         } else {
677           Goto(&ok_to_write);
678         }
679       }
680     }
681 
682     BIND(&next_proto);
683     // Bailout if it can be an integer indexed exotic case.
684     GotoIf(IsJSTypedArrayInstanceType(instance_type), bailout);
685     TNode<HeapObject> proto = LoadMapPrototype(holder_map);
686     GotoIf(IsNull(proto), &ok_to_write);
687     var_holder = proto;
688     var_holder_map = LoadMap(proto);
689     Goto(&loop);
690   }
691   BIND(&ok_to_write);
692 }
693 
FindCandidateStoreICTransitionMapHandler(TNode<Map> map,TNode<Name> name,Label * slow)694 TNode<Map> KeyedStoreGenericAssembler::FindCandidateStoreICTransitionMapHandler(
695     TNode<Map> map, TNode<Name> name, Label* slow) {
696   TVARIABLE(Map, var_transition_map);
697   Label simple_transition(this), transition_array(this),
698       found_handler_candidate(this);
699 
700   TNode<MaybeObject> maybe_handler =
701       LoadMaybeWeakObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
702 
703   // Smi -> slow,
704   // Cleared weak reference -> slow
705   // weak reference -> simple_transition
706   // strong reference -> transition_array
707   TVARIABLE(Object, var_transition_map_or_array);
708   DispatchMaybeObject(maybe_handler, slow, slow, &simple_transition,
709                       &transition_array, &var_transition_map_or_array);
710 
711   BIND(&simple_transition);
712   {
713     var_transition_map = CAST(var_transition_map_or_array.value());
714     Goto(&found_handler_candidate);
715   }
716 
717   BIND(&transition_array);
718   {
719     TNode<Map> maybe_handler_map =
720         LoadMap(CAST(var_transition_map_or_array.value()));
721     GotoIfNot(IsTransitionArrayMap(maybe_handler_map), slow);
722 
723     TVARIABLE(IntPtrT, var_name_index);
724     Label if_found_candidate(this);
725     TNode<TransitionArray> transitions =
726         CAST(var_transition_map_or_array.value());
727     TransitionLookup(name, transitions, &if_found_candidate, &var_name_index,
728                      slow);
729 
730     BIND(&if_found_candidate);
731     {
732       // Given that
733       // 1) transitions with the same name are ordered in the transition
734       //    array by PropertyKind and then by PropertyAttributes values,
735       // 2) kData < kAccessor,
736       // 3) NONE == 0,
737       // 4) properties with private symbol names are guaranteed to be
738       //    non-enumerable (so DONT_ENUM bit in attributes is always set),
739       // the resulting map of transitioning store if it exists in the
740       // transition array is expected to be the first among the transitions
741       // with the same name.
742       // See TransitionArray::CompareDetails() for details.
743       STATIC_ASSERT(kData == 0);
744       STATIC_ASSERT(NONE == 0);
745       const int kKeyToTargetOffset = (TransitionArray::kEntryTargetIndex -
746                                       TransitionArray::kEntryKeyIndex) *
747                                      kTaggedSize;
748       var_transition_map = CAST(GetHeapObjectAssumeWeak(
749           LoadArrayElement(transitions, WeakFixedArray::kHeaderSize,
750                            var_name_index.value(), kKeyToTargetOffset)));
751       Goto(&found_handler_candidate);
752     }
753   }
754 
755   BIND(&found_handler_candidate);
756   return var_transition_map.value();
757 }
758 
EmitGenericPropertyStore(TNode<JSReceiver> receiver,TNode<Map> receiver_map,const StoreICParameters * p,ExitPoint * exit_point,Label * slow,Maybe<LanguageMode> maybe_language_mode)759 void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
760     TNode<JSReceiver> receiver, TNode<Map> receiver_map,
761     const StoreICParameters* p, ExitPoint* exit_point, Label* slow,
762     Maybe<LanguageMode> maybe_language_mode) {
763   CSA_DCHECK(this, IsSimpleObjectMap(receiver_map));
764   // TODO(rmcilroy) Type as Struct once we use a trimmed down
765   // LoadAccessorFromFastObject instead of LoadPropertyFromFastObject.
766   TVARIABLE(Object, var_accessor_pair);
767   TVARIABLE(HeapObject, var_accessor_holder);
768   Label fast_properties(this), dictionary_properties(this), accessor(this),
769       readonly(this);
770   TNode<Uint32T> bitfield3 = LoadMapBitField3(receiver_map);
771   TNode<Name> name = CAST(p->name());
772   Branch(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bitfield3),
773          &dictionary_properties, &fast_properties);
774 
775   BIND(&fast_properties);
776   {
777     Comment("fast property store");
778     TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
779     Label descriptor_found(this), lookup_transition(this);
780     TVARIABLE(IntPtrT, var_name_index);
781     DescriptorLookup(name, descriptors, bitfield3, &descriptor_found,
782                      &var_name_index, &lookup_transition);
783 
784     BIND(&descriptor_found);
785     {
786       TNode<IntPtrT> name_index = var_name_index.value();
787       TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index);
788       Label data_property(this);
789       JumpIfDataProperty(details, &data_property,
790                          ShouldReconfigureExisting() ? nullptr : &readonly);
791 
792       if (ShouldCallSetter()) {
793         // Accessor case.
794         // TODO(jkummerow): Implement a trimmed-down LoadAccessorFromFastObject.
795         LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
796                                    name_index, details, &var_accessor_pair);
797         var_accessor_holder = receiver;
798         Goto(&accessor);
799       } else {
800         // Handle accessor to data property reconfiguration in runtime.
801         Goto(slow);
802       }
803 
804       BIND(&data_property);
805       {
806         CheckForAssociatedProtector(name, slow);
807         OverwriteExistingFastDataProperty(receiver, receiver_map, descriptors,
808                                           name_index, details, p->value(), slow,
809                                           false);
810         exit_point->Return(p->value());
811       }
812     }
813     BIND(&lookup_transition);
814     {
815       Comment("lookup transition");
816       CheckForAssociatedProtector(name, slow);
817       TNode<Map> transition_map =
818           FindCandidateStoreICTransitionMapHandler(receiver_map, name, slow);
819 
820       // Validate the transition handler candidate and apply the transition.
821       StoreTransitionMapFlags flags = kValidateTransitionHandler;
822       if (ShouldCheckPrototypeValidity()) {
823         flags = StoreTransitionMapFlags(flags | kCheckPrototypeValidity);
824       }
825       HandleStoreICTransitionMapHandlerCase(p, transition_map, slow, flags);
826       exit_point->Return(p->value());
827     }
828   }
829 
830   BIND(&dictionary_properties);
831   {
832     Comment("dictionary property store");
833     // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
834     // seeing global objects here (which would need special handling).
835 
836     TVARIABLE(IntPtrT, var_name_index);
837     Label dictionary_found(this, &var_name_index), not_found(this);
838     TNode<PropertyDictionary> properties = CAST(LoadSlowProperties(receiver));
839     NameDictionaryLookup<PropertyDictionary>(
840         properties, name, &dictionary_found, &var_name_index, &not_found);
841     BIND(&dictionary_found);
842     {
843       Label check_const(this), overwrite(this), done(this);
844       TNode<Uint32T> details =
845           LoadDetailsByKeyIndex(properties, var_name_index.value());
846       JumpIfDataProperty(details, &check_const,
847                          ShouldReconfigureExisting() ? nullptr : &readonly);
848 
849       if (ShouldCallSetter()) {
850         // Accessor case.
851         var_accessor_pair =
852             LoadValueByKeyIndex(properties, var_name_index.value());
853         var_accessor_holder = receiver;
854         Goto(&accessor);
855       } else {
856         // We must reconfigure an accessor property to a data property
857         // here, let the runtime take care of that.
858         Goto(slow);
859       }
860 
861       BIND(&check_const);
862       {
863         if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) {
864           GotoIfNot(IsPropertyDetailsConst(details), &overwrite);
865           TNode<Object> prev_value =
866               LoadValueByKeyIndex(properties, var_name_index.value());
867 
868           BranchIfSameValue(prev_value, p->value(), &done, slow,
869                             SameValueMode::kNumbersOnly);
870         } else {
871           Goto(&overwrite);
872         }
873       }
874 
875       BIND(&overwrite);
876       {
877         CheckForAssociatedProtector(name, slow);
878         StoreValueByKeyIndex<PropertyDictionary>(
879             properties, var_name_index.value(), p->value());
880         Goto(&done);
881       }
882 
883       BIND(&done);
884       exit_point->Return(p->value());
885     }
886 
887     BIND(&not_found);
888     {
889       // TODO(jkummerow): Also add support to correctly handle integer exotic
890       // cases for typed arrays and remove this check here.
891       GotoIf(IsJSTypedArrayMap(receiver_map), slow);
892       CheckForAssociatedProtector(name, slow);
893       Label extensible(this), is_private_symbol(this);
894       GotoIf(IsPrivateSymbol(name), &is_private_symbol);
895       Branch(IsSetWord32<Map::Bits3::IsExtensibleBit>(bitfield3), &extensible,
896              slow);
897 
898       BIND(&is_private_symbol);
899       {
900         CSA_DCHECK(this, IsPrivateSymbol(name));
901         // For private names, we miss to the runtime which will throw.
902         // For private symbols, we extend and store an own property.
903         Branch(IsPrivateName(CAST(name)), slow, &extensible);
904       }
905 
906       BIND(&extensible);
907       if (ShouldCheckPrototype()) {
908         DCHECK(ShouldCallSetter());
909         LookupPropertyOnPrototypeChain(
910             receiver_map, name, &accessor, &var_accessor_pair,
911             &var_accessor_holder,
912             ShouldReconfigureExisting() ? nullptr : &readonly, slow);
913       }
914       Label add_dictionary_property_slow(this);
915       InvalidateValidityCellIfPrototype(receiver_map, bitfield3);
916       Add<PropertyDictionary>(properties, name, p->value(),
917                               &add_dictionary_property_slow);
918       exit_point->Return(p->value());
919 
920       BIND(&add_dictionary_property_slow);
921       exit_point->ReturnCallRuntime(Runtime::kAddDictionaryProperty,
922                                     p->context(), p->receiver(), name,
923                                     p->value());
924     }
925   }
926 
927   if (ShouldCallSetter()) {
928     BIND(&accessor);
929     {
930       Label not_callable(this);
931       TNode<Struct> accessor_pair = CAST(var_accessor_pair.value());
932       GotoIf(IsAccessorInfo(accessor_pair), slow);
933       CSA_DCHECK(this, IsAccessorPair(accessor_pair));
934       TNode<HeapObject> setter =
935           CAST(LoadObjectField(accessor_pair, AccessorPair::kSetterOffset));
936       TNode<Map> setter_map = LoadMap(setter);
937       // FunctionTemplateInfo setters are not supported yet.
938       GotoIf(IsFunctionTemplateInfoMap(setter_map), slow);
939       GotoIfNot(IsCallableMap(setter_map), &not_callable);
940 
941       Call(p->context(), setter, receiver, p->value());
942       exit_point->Return(p->value());
943 
944       BIND(&not_callable);
945       {
946         LanguageMode language_mode;
947         if (maybe_language_mode.To(&language_mode)) {
948           if (language_mode == LanguageMode::kStrict) {
949             exit_point->ReturnCallRuntime(
950                 Runtime::kThrowTypeError, p->context(),
951                 SmiConstant(MessageTemplate::kNoSetterInCallback), name,
952                 var_accessor_holder.value());
953           } else {
954             exit_point->Return(p->value());
955           }
956         } else {
957           CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context(),
958                       SmiConstant(MessageTemplate::kNoSetterInCallback), name,
959                       var_accessor_holder.value());
960           exit_point->Return(p->value());
961         }
962       }
963     }
964   }
965 
966   if (!ShouldReconfigureExisting()) {
967     BIND(&readonly);
968     {
969       LanguageMode language_mode;
970       if (maybe_language_mode.To(&language_mode)) {
971         if (language_mode == LanguageMode::kStrict) {
972           TNode<String> type = Typeof(p->receiver());
973           ThrowTypeError(p->context(), MessageTemplate::kStrictReadOnlyProperty,
974                          name, type, p->receiver());
975         } else {
976           exit_point->Return(p->value());
977         }
978       } else {
979         CallRuntime(Runtime::kThrowTypeErrorIfStrict, p->context(),
980                     SmiConstant(MessageTemplate::kStrictReadOnlyProperty), name,
981                     Typeof(p->receiver()), p->receiver());
982         exit_point->Return(p->value());
983       }
984     }
985   }
986 }
987 
988 // Helper that is used by the public KeyedStoreGeneric and by SetProperty.
KeyedStoreGeneric(TNode<Context> context,TNode<Object> receiver_maybe_smi,TNode<Object> key,TNode<Object> value,Maybe<LanguageMode> language_mode)989 void KeyedStoreGenericAssembler::KeyedStoreGeneric(
990     TNode<Context> context, TNode<Object> receiver_maybe_smi, TNode<Object> key,
991     TNode<Object> value, Maybe<LanguageMode> language_mode) {
992   TVARIABLE(IntPtrT, var_index);
993   TVARIABLE(Name, var_unique);
994   Label if_index(this, &var_index), if_unique_name(this),
995       not_internalized(this), slow(this);
996 
997   GotoIf(TaggedIsSmi(receiver_maybe_smi), &slow);
998   TNode<HeapObject> receiver = CAST(receiver_maybe_smi);
999   TNode<Map> receiver_map = LoadMap(receiver);
1000   TNode<Uint16T> instance_type = LoadMapInstanceType(receiver_map);
1001   // Receivers requiring non-standard element accesses (interceptors, access
1002   // checks, strings and string wrappers, proxies) are handled in the runtime.
1003   GotoIf(IsCustomElementsReceiverInstanceType(instance_type), &slow);
1004 
1005   TryToName(key, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
1006             &not_internalized);
1007 
1008   BIND(&if_index);
1009   {
1010     Comment("integer index");
1011     EmitGenericElementStore(CAST(receiver), receiver_map, instance_type,
1012                             var_index.value(), value, context, &slow);
1013   }
1014 
1015   BIND(&if_unique_name);
1016   {
1017     Comment("key is unique name");
1018     StoreICParameters p(context, receiver, var_unique.value(), value, {},
1019                         UndefinedConstant());
1020     ExitPoint direct_exit(this);
1021     EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &direct_exit,
1022                              &slow, language_mode);
1023   }
1024 
1025   BIND(&not_internalized);
1026   {
1027     if (FLAG_internalize_on_the_fly) {
1028       TryInternalizeString(CAST(key), &if_index, &var_index, &if_unique_name,
1029                            &var_unique, &slow, &slow);
1030     } else {
1031       Goto(&slow);
1032     }
1033   }
1034 
1035   BIND(&slow);
1036   {
1037     if (IsKeyedStore()) {
1038       Comment("KeyedStoreGeneric_slow");
1039       TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key,
1040                       value);
1041     } else {
1042       DCHECK(IsStoreInLiteral());
1043       TailCallRuntime(Runtime::kStoreDataPropertyInLiteral, context, receiver,
1044                       key, value);
1045     }
1046   }
1047 }
1048 
KeyedStoreGeneric()1049 void KeyedStoreGenericAssembler::KeyedStoreGeneric() {
1050   using Descriptor = StoreDescriptor;
1051 
1052   auto receiver = Parameter<Object>(Descriptor::kReceiver);
1053   auto name = Parameter<Object>(Descriptor::kName);
1054   auto value = Parameter<Object>(Descriptor::kValue);
1055   auto context = Parameter<Context>(Descriptor::kContext);
1056 
1057   KeyedStoreGeneric(context, receiver, name, value, Nothing<LanguageMode>());
1058 }
1059 
SetProperty(TNode<Context> context,TNode<Object> receiver,TNode<Object> key,TNode<Object> value,LanguageMode language_mode)1060 void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
1061                                              TNode<Object> receiver,
1062                                              TNode<Object> key,
1063                                              TNode<Object> value,
1064                                              LanguageMode language_mode) {
1065   KeyedStoreGeneric(context, receiver, key, value, Just(language_mode));
1066 }
1067 
StoreIC_NoFeedback()1068 void KeyedStoreGenericAssembler::StoreIC_NoFeedback() {
1069   using Descriptor = StoreDescriptor;
1070 
1071   auto receiver_maybe_smi = Parameter<Object>(Descriptor::kReceiver);
1072   auto name = Parameter<Object>(Descriptor::kName);
1073   auto value = Parameter<Object>(Descriptor::kValue);
1074   auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
1075   auto context = Parameter<Context>(Descriptor::kContext);
1076 
1077   Label miss(this, Label::kDeferred), store_property(this);
1078 
1079   GotoIf(TaggedIsSmi(receiver_maybe_smi), &miss);
1080 
1081   {
1082     TNode<HeapObject> receiver = CAST(receiver_maybe_smi);
1083     TNode<Map> receiver_map = LoadMap(receiver);
1084     TNode<Uint16T> instance_type = LoadMapInstanceType(receiver_map);
1085     // Receivers requiring non-standard element accesses (interceptors, access
1086     // checks, strings and string wrappers, proxies) are handled in the runtime.
1087     GotoIf(IsSpecialReceiverInstanceType(instance_type), &miss);
1088     {
1089       StoreICParameters p(context, receiver, name, value, slot,
1090                           UndefinedConstant());
1091       EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &miss);
1092     }
1093   }
1094 
1095   BIND(&miss);
1096   {
1097     TailCallRuntime(Runtime::kStoreIC_Miss, context, value, slot,
1098                     UndefinedConstant(), receiver_maybe_smi, name);
1099   }
1100 }
1101 
SetProperty(TNode<Context> context,TNode<JSReceiver> receiver,TNode<BoolT> is_simple_receiver,TNode<Name> unique_name,TNode<Object> value,LanguageMode language_mode)1102 void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
1103                                              TNode<JSReceiver> receiver,
1104                                              TNode<BoolT> is_simple_receiver,
1105                                              TNode<Name> unique_name,
1106                                              TNode<Object> value,
1107                                              LanguageMode language_mode) {
1108   StoreICParameters p(context, receiver, unique_name, value, {},
1109                       UndefinedConstant());
1110 
1111   Label done(this), slow(this, Label::kDeferred);
1112   ExitPoint exit_point(this, [&](TNode<Object> result) { Goto(&done); });
1113 
1114   CSA_DCHECK(this, Word32Equal(is_simple_receiver,
1115                                IsSimpleObjectMap(LoadMap(receiver))));
1116   GotoIfNot(is_simple_receiver, &slow);
1117 
1118   EmitGenericPropertyStore(receiver, LoadMap(receiver), &p, &exit_point, &slow,
1119                            Just(language_mode));
1120 
1121   BIND(&slow);
1122   {
1123     if (IsStoreInLiteral()) {
1124       CallRuntime(Runtime::kStoreDataPropertyInLiteral, context, receiver,
1125                   unique_name, value);
1126     } else {
1127       CallRuntime(Runtime::kSetKeyedProperty, context, receiver, unique_name,
1128                   value);
1129     }
1130     Goto(&done);
1131   }
1132 
1133   BIND(&done);
1134 }
1135 
1136 }  // namespace internal
1137 }  // namespace v8
1138