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, ¬_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(¬_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), ¬_callable);
940
941 Call(p->context(), setter, receiver, p->value());
942 exit_point->Return(p->value());
943
944 BIND(¬_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 ¬_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(¬_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