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