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