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