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