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/accessor-assembler.h"
6
7 #include "src/code-factory.h"
8 #include "src/code-stubs.h"
9 #include "src/counters.h"
10 #include "src/ic/handler-configuration.h"
11 #include "src/ic/stub-cache.h"
12 #include "src/objects-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
17 using compiler::CodeAssemblerState;
18 using compiler::Node;
19
20 //////////////////// Private helpers.
21
TryMonomorphicCase(Node * slot,Node * vector,Node * receiver_map,Label * if_handler,Variable * var_handler,Label * if_miss)22 Node* AccessorAssembler::TryMonomorphicCase(Node* slot, Node* vector,
23 Node* receiver_map,
24 Label* if_handler,
25 Variable* var_handler,
26 Label* if_miss) {
27 Comment("TryMonomorphicCase");
28 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
29
30 // TODO(ishell): add helper class that hides offset computations for a series
31 // of loads.
32 int32_t header_size = FixedArray::kHeaderSize - kHeapObjectTag;
33 // Adding |header_size| with a separate IntPtrAdd rather than passing it
34 // into ElementOffsetFromIndex() allows it to be folded into a single
35 // [base, index, offset] indirect memory access on x64.
36 Node* offset =
37 ElementOffsetFromIndex(slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS);
38 Node* feedback = Load(MachineType::AnyTagged(), vector,
39 IntPtrAdd(offset, IntPtrConstant(header_size)));
40
41 // Try to quickly handle the monomorphic case without knowing for sure
42 // if we have a weak cell in feedback. We do know it's safe to look
43 // at WeakCell::kValueOffset.
44 GotoIf(WordNotEqual(receiver_map, LoadWeakCellValueUnchecked(feedback)),
45 if_miss);
46
47 Node* handler =
48 Load(MachineType::AnyTagged(), vector,
49 IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize)));
50
51 var_handler->Bind(handler);
52 Goto(if_handler);
53 return feedback;
54 }
55
HandlePolymorphicCase(Node * receiver_map,Node * feedback,Label * if_handler,Variable * var_handler,Label * if_miss,int unroll_count)56 void AccessorAssembler::HandlePolymorphicCase(Node* receiver_map,
57 Node* feedback, Label* if_handler,
58 Variable* var_handler,
59 Label* if_miss,
60 int unroll_count) {
61 Comment("HandlePolymorphicCase");
62 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
63
64 // Iterate {feedback} array.
65 const int kEntrySize = 2;
66
67 for (int i = 0; i < unroll_count; i++) {
68 Label next_entry(this);
69 Node* cached_map =
70 LoadWeakCellValue(LoadFixedArrayElement(feedback, i * kEntrySize));
71 GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
72
73 // Found, now call handler.
74 Node* handler = LoadFixedArrayElement(feedback, i * kEntrySize + 1);
75 var_handler->Bind(handler);
76 Goto(if_handler);
77
78 Bind(&next_entry);
79 }
80
81 // Loop from {unroll_count}*kEntrySize to {length}.
82 Node* init = IntPtrConstant(unroll_count * kEntrySize);
83 Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
84 BuildFastLoop(
85 init, length,
86 [this, receiver_map, feedback, if_handler, var_handler](Node* index) {
87 Node* cached_map =
88 LoadWeakCellValue(LoadFixedArrayElement(feedback, index));
89
90 Label next_entry(this);
91 GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
92
93 // Found, now call handler.
94 Node* handler = LoadFixedArrayElement(feedback, index, kPointerSize);
95 var_handler->Bind(handler);
96 Goto(if_handler);
97
98 Bind(&next_entry);
99 },
100 kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
101 // The loop falls through if no handler was found.
102 Goto(if_miss);
103 }
104
HandleKeyedStorePolymorphicCase(Node * receiver_map,Node * feedback,Label * if_handler,Variable * var_handler,Label * if_transition_handler,Variable * var_transition_map_cell,Label * if_miss)105 void AccessorAssembler::HandleKeyedStorePolymorphicCase(
106 Node* receiver_map, Node* feedback, Label* if_handler,
107 Variable* var_handler, Label* if_transition_handler,
108 Variable* var_transition_map_cell, Label* if_miss) {
109 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
110 DCHECK_EQ(MachineRepresentation::kTagged, var_transition_map_cell->rep());
111
112 const int kEntrySize = 3;
113
114 Node* init = IntPtrConstant(0);
115 Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
116 BuildFastLoop(init, length,
117 [this, receiver_map, feedback, if_handler, var_handler,
118 if_transition_handler, var_transition_map_cell](Node* index) {
119 Node* cached_map =
120 LoadWeakCellValue(LoadFixedArrayElement(feedback, index));
121 Label next_entry(this);
122 GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
123
124 Node* maybe_transition_map_cell =
125 LoadFixedArrayElement(feedback, index, kPointerSize);
126
127 var_handler->Bind(
128 LoadFixedArrayElement(feedback, index, 2 * kPointerSize));
129 GotoIf(WordEqual(maybe_transition_map_cell,
130 LoadRoot(Heap::kUndefinedValueRootIndex)),
131 if_handler);
132 var_transition_map_cell->Bind(maybe_transition_map_cell);
133 Goto(if_transition_handler);
134
135 Bind(&next_entry);
136 },
137 kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
138 // The loop falls through if no handler was found.
139 Goto(if_miss);
140 }
141
HandleLoadICHandlerCase(const LoadICParameters * p,Node * handler,Label * miss,ElementSupport support_elements)142 void AccessorAssembler::HandleLoadICHandlerCase(
143 const LoadICParameters* p, Node* handler, Label* miss,
144 ElementSupport support_elements) {
145 Comment("have_handler");
146 ExitPoint direct_exit(this);
147
148 Variable var_holder(this, MachineRepresentation::kTagged);
149 var_holder.Bind(p->receiver);
150 Variable var_smi_handler(this, MachineRepresentation::kTagged);
151 var_smi_handler.Bind(handler);
152
153 Variable* vars[] = {&var_holder, &var_smi_handler};
154 Label if_smi_handler(this, 2, vars);
155 Label try_proto_handler(this), call_handler(this);
156
157 Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
158
159 // |handler| is a Smi, encoding what to do. See SmiHandler methods
160 // for the encoding format.
161 Bind(&if_smi_handler);
162 {
163 HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(),
164 miss, &direct_exit, support_elements);
165 }
166
167 Bind(&try_proto_handler);
168 {
169 GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
170 HandleLoadICProtoHandlerCase(p, handler, &var_holder, &var_smi_handler,
171 &if_smi_handler, miss, &direct_exit, false);
172 }
173
174 Bind(&call_handler);
175 {
176 typedef LoadWithVectorDescriptor Descriptor;
177 TailCallStub(Descriptor(isolate()), handler, p->context, p->receiver,
178 p->name, p->slot, p->vector);
179 }
180 }
181
HandleLoadICSmiHandlerCase(const LoadICParameters * p,Node * holder,Node * smi_handler,Label * miss,ExitPoint * exit_point,ElementSupport support_elements)182 void AccessorAssembler::HandleLoadICSmiHandlerCase(
183 const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss,
184 ExitPoint* exit_point, ElementSupport support_elements) {
185 Variable var_double_value(this, MachineRepresentation::kFloat64);
186 Label rebox_double(this, &var_double_value);
187
188 Node* handler_word = SmiUntag(smi_handler);
189 Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
190 if (support_elements == kSupportElements) {
191 Label property(this);
192 GotoIfNot(
193 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForElements)),
194 &property);
195
196 Comment("element_load");
197 Node* intptr_index = TryToIntptr(p->name, miss);
198 Node* elements = LoadElements(holder);
199 Node* is_jsarray_condition =
200 IsSetWord<LoadHandler::IsJsArrayBits>(handler_word);
201 Node* elements_kind =
202 DecodeWord32FromWord<LoadHandler::ElementsKindBits>(handler_word);
203 Label if_hole(this), unimplemented_elements_kind(this);
204 Label* out_of_bounds = miss;
205 EmitElementLoad(holder, elements, elements_kind, intptr_index,
206 is_jsarray_condition, &if_hole, &rebox_double,
207 &var_double_value, &unimplemented_elements_kind,
208 out_of_bounds, miss, exit_point);
209
210 Bind(&unimplemented_elements_kind);
211 {
212 // Smi handlers should only be installed for supported elements kinds.
213 // Crash if we get here.
214 DebugBreak();
215 Goto(miss);
216 }
217
218 Bind(&if_hole);
219 {
220 Comment("convert hole");
221 GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
222 Node* protector_cell = LoadRoot(Heap::kArrayProtectorRootIndex);
223 DCHECK(isolate()->heap()->array_protector()->IsPropertyCell());
224 GotoIfNot(
225 WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
226 SmiConstant(Smi::FromInt(Isolate::kProtectorValid))),
227 miss);
228 exit_point->Return(UndefinedConstant());
229 }
230
231 Bind(&property);
232 Comment("property_load");
233 }
234
235 Label constant(this), field(this);
236 Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForFields)),
237 &field, &constant);
238
239 Bind(&field);
240 {
241 Comment("field_load");
242 Node* offset = DecodeWord<LoadHandler::FieldOffsetBits>(handler_word);
243
244 Label inobject(this), out_of_object(this);
245 Branch(IsSetWord<LoadHandler::IsInobjectBits>(handler_word), &inobject,
246 &out_of_object);
247
248 Bind(&inobject);
249 {
250 Label is_double(this);
251 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
252 exit_point->Return(LoadObjectField(holder, offset));
253
254 Bind(&is_double);
255 if (FLAG_unbox_double_fields) {
256 var_double_value.Bind(
257 LoadObjectField(holder, offset, MachineType::Float64()));
258 } else {
259 Node* mutable_heap_number = LoadObjectField(holder, offset);
260 var_double_value.Bind(LoadHeapNumberValue(mutable_heap_number));
261 }
262 Goto(&rebox_double);
263 }
264
265 Bind(&out_of_object);
266 {
267 Label is_double(this);
268 Node* properties = LoadProperties(holder);
269 Node* value = LoadObjectField(properties, offset);
270 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
271 exit_point->Return(value);
272
273 Bind(&is_double);
274 var_double_value.Bind(LoadHeapNumberValue(value));
275 Goto(&rebox_double);
276 }
277
278 Bind(&rebox_double);
279 exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value()));
280 }
281
282 Bind(&constant);
283 {
284 Comment("constant_load");
285 Node* descriptors = LoadMapDescriptors(LoadMap(holder));
286 Node* descriptor =
287 DecodeWord<LoadHandler::DescriptorValueIndexBits>(handler_word);
288 CSA_ASSERT(this,
289 UintPtrLessThan(descriptor,
290 LoadAndUntagFixedArrayBaseLength(descriptors)));
291 Node* value = LoadFixedArrayElement(descriptors, descriptor);
292
293 Label if_accessor_info(this);
294 GotoIf(IsSetWord<LoadHandler::IsAccessorInfoBits>(handler_word),
295 &if_accessor_info);
296 exit_point->Return(value);
297
298 Bind(&if_accessor_info);
299 Callable callable = CodeFactory::ApiGetter(isolate());
300 exit_point->ReturnCallStub(callable, p->context, p->receiver, holder,
301 value);
302 }
303 }
304
HandleLoadICProtoHandlerCase(const LoadICParameters * p,Node * handler,Variable * var_holder,Variable * var_smi_handler,Label * if_smi_handler,Label * miss,ExitPoint * exit_point,bool throw_reference_error_if_nonexistent)305 void AccessorAssembler::HandleLoadICProtoHandlerCase(
306 const LoadICParameters* p, Node* handler, Variable* var_holder,
307 Variable* var_smi_handler, Label* if_smi_handler, Label* miss,
308 ExitPoint* exit_point, bool throw_reference_error_if_nonexistent) {
309 DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
310 DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
311
312 // IC dispatchers rely on these assumptions to be held.
313 STATIC_ASSERT(FixedArray::kLengthOffset == LoadHandler::kHolderCellOffset);
314 DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kSmiHandlerIndex),
315 LoadHandler::kSmiHandlerOffset);
316 DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kValidityCellIndex),
317 LoadHandler::kValidityCellOffset);
318
319 // Both FixedArray and Tuple3 handlers have validity cell at the same offset.
320 Label validity_cell_check_done(this);
321 Node* validity_cell =
322 LoadObjectField(handler, LoadHandler::kValidityCellOffset);
323 GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
324 &validity_cell_check_done);
325 Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
326 GotoIf(WordNotEqual(cell_value,
327 SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
328 miss);
329 Goto(&validity_cell_check_done);
330
331 Bind(&validity_cell_check_done);
332 Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
333 CSA_ASSERT(this, TaggedIsSmi(smi_handler));
334 Node* handler_flags = SmiUntag(smi_handler);
335
336 Label check_prototypes(this);
337 GotoIfNot(
338 IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>(handler_flags),
339 &check_prototypes);
340 {
341 CSA_ASSERT(this, Word32BinaryNot(
342 HasInstanceType(p->receiver, JS_GLOBAL_OBJECT_TYPE)));
343 // We have a dictionary receiver, do a negative lookup check.
344 NameDictionaryNegativeLookup(p->receiver, p->name, miss);
345 Goto(&check_prototypes);
346 }
347
348 Bind(&check_prototypes);
349 Node* maybe_holder_cell =
350 LoadObjectField(handler, LoadHandler::kHolderCellOffset);
351 Label array_handler(this), tuple_handler(this);
352 Branch(TaggedIsSmi(maybe_holder_cell), &array_handler, &tuple_handler);
353
354 Bind(&tuple_handler);
355 {
356 Label load_existent(this);
357 GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
358 // This is a handler for a load of a non-existent value.
359 if (throw_reference_error_if_nonexistent) {
360 exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
361 p->name);
362 } else {
363 exit_point->Return(UndefinedConstant());
364 }
365
366 Bind(&load_existent);
367 Node* holder = LoadWeakCellValue(maybe_holder_cell);
368 // The |holder| is guaranteed to be alive at this point since we passed
369 // both the receiver map check and the validity cell check.
370 CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
371
372 var_holder->Bind(holder);
373 var_smi_handler->Bind(smi_handler);
374 Goto(if_smi_handler);
375 }
376
377 Bind(&array_handler);
378 {
379 exit_point->ReturnCallStub(
380 CodeFactory::LoadICProtoArray(isolate(),
381 throw_reference_error_if_nonexistent),
382 p->context, p->receiver, p->name, p->slot, p->vector, handler);
383 }
384 }
385
EmitLoadICProtoArrayCheck(const LoadICParameters * p,Node * handler,Node * handler_length,Node * handler_flags,Label * miss,bool throw_reference_error_if_nonexistent)386 Node* AccessorAssembler::EmitLoadICProtoArrayCheck(
387 const LoadICParameters* p, Node* handler, Node* handler_length,
388 Node* handler_flags, Label* miss,
389 bool throw_reference_error_if_nonexistent) {
390 Variable start_index(this, MachineType::PointerRepresentation());
391 start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
392
393 Label can_access(this);
394 GotoIfNot(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
395 &can_access);
396 {
397 // Skip this entry of a handler.
398 start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
399
400 int offset =
401 FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex);
402 Node* expected_native_context =
403 LoadWeakCellValue(LoadObjectField(handler, offset), miss);
404 CSA_ASSERT(this, IsNativeContext(expected_native_context));
405
406 Node* native_context = LoadNativeContext(p->context);
407 GotoIf(WordEqual(expected_native_context, native_context), &can_access);
408 // If the receiver is not a JSGlobalProxy then we miss.
409 GotoIfNot(IsJSGlobalProxy(p->receiver), miss);
410 // For JSGlobalProxy receiver try to compare security tokens of current
411 // and expected native contexts.
412 Node* expected_token = LoadContextElement(expected_native_context,
413 Context::SECURITY_TOKEN_INDEX);
414 Node* current_token =
415 LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
416 Branch(WordEqual(expected_token, current_token), &can_access, miss);
417 }
418 Bind(&can_access);
419
420 BuildFastLoop(start_index.value(), handler_length,
421 [this, p, handler, miss](Node* current) {
422 Node* prototype_cell =
423 LoadFixedArrayElement(handler, current);
424 CheckPrototype(prototype_cell, p->name, miss);
425 },
426 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
427
428 Node* maybe_holder_cell =
429 LoadFixedArrayElement(handler, LoadHandler::kHolderCellIndex);
430 Label load_existent(this);
431 GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
432 // This is a handler for a load of a non-existent value.
433 if (throw_reference_error_if_nonexistent) {
434 TailCallRuntime(Runtime::kThrowReferenceError, p->context, p->name);
435 } else {
436 Return(UndefinedConstant());
437 }
438
439 Bind(&load_existent);
440 Node* holder = LoadWeakCellValue(maybe_holder_cell);
441 // The |holder| is guaranteed to be alive at this point since we passed
442 // the receiver map check, the validity cell check and the prototype chain
443 // check.
444 CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
445 return holder;
446 }
447
HandleLoadGlobalICHandlerCase(const LoadICParameters * pp,Node * handler,Label * miss,ExitPoint * exit_point,bool throw_reference_error_if_nonexistent)448 void AccessorAssembler::HandleLoadGlobalICHandlerCase(
449 const LoadICParameters* pp, Node* handler, Label* miss,
450 ExitPoint* exit_point, bool throw_reference_error_if_nonexistent) {
451 LoadICParameters p = *pp;
452 DCHECK_NULL(p.receiver);
453 Node* native_context = LoadNativeContext(p.context);
454 p.receiver = LoadContextElement(native_context, Context::EXTENSION_INDEX);
455
456 Variable var_holder(this, MachineRepresentation::kTagged);
457 Variable var_smi_handler(this, MachineRepresentation::kTagged);
458 Label if_smi_handler(this);
459 HandleLoadICProtoHandlerCase(&p, handler, &var_holder, &var_smi_handler,
460 &if_smi_handler, miss, exit_point,
461 throw_reference_error_if_nonexistent);
462 Bind(&if_smi_handler);
463 HandleLoadICSmiHandlerCase(&p, var_holder.value(), var_smi_handler.value(),
464 miss, exit_point, kOnlyProperties);
465 }
466
HandleStoreICHandlerCase(const StoreICParameters * p,Node * handler,Label * miss,ElementSupport support_elements)467 void AccessorAssembler::HandleStoreICHandlerCase(
468 const StoreICParameters* p, Node* handler, Label* miss,
469 ElementSupport support_elements) {
470 Label if_smi_handler(this), if_nonsmi_handler(this);
471 Label if_proto_handler(this), if_element_handler(this), call_handler(this);
472
473 Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler);
474
475 // |handler| is a Smi, encoding what to do. See SmiHandler methods
476 // for the encoding format.
477 Bind(&if_smi_handler);
478 {
479 Node* holder = p->receiver;
480 Node* handler_word = SmiUntag(handler);
481
482 // Handle non-transitioning field stores.
483 HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
484 }
485
486 Bind(&if_nonsmi_handler);
487 {
488 Node* handler_map = LoadMap(handler);
489 if (support_elements == kSupportElements) {
490 GotoIf(IsTuple2Map(handler_map), &if_element_handler);
491 }
492 Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler);
493 }
494
495 if (support_elements == kSupportElements) {
496 Bind(&if_element_handler);
497 { HandleStoreICElementHandlerCase(p, handler, miss); }
498 }
499
500 Bind(&if_proto_handler);
501 {
502 HandleStoreICProtoHandler(p, handler, miss);
503 }
504
505 // |handler| is a heap object. Must be code, call it.
506 Bind(&call_handler);
507 {
508 StoreWithVectorDescriptor descriptor(isolate());
509 TailCallStub(descriptor, handler, p->context, p->receiver, p->name,
510 p->value, p->slot, p->vector);
511 }
512 }
513
HandleStoreICElementHandlerCase(const StoreICParameters * p,Node * handler,Label * miss)514 void AccessorAssembler::HandleStoreICElementHandlerCase(
515 const StoreICParameters* p, Node* handler, Label* miss) {
516 Comment("HandleStoreICElementHandlerCase");
517 Node* validity_cell = LoadObjectField(handler, Tuple2::kValue1Offset);
518 Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
519 GotoIf(WordNotEqual(cell_value,
520 SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
521 miss);
522
523 Node* code_handler = LoadObjectField(handler, Tuple2::kValue2Offset);
524 CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
525
526 StoreWithVectorDescriptor descriptor(isolate());
527 TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name,
528 p->value, p->slot, p->vector);
529 }
530
HandleStoreICProtoHandler(const StoreICParameters * p,Node * handler,Label * miss)531 void AccessorAssembler::HandleStoreICProtoHandler(const StoreICParameters* p,
532 Node* handler, Label* miss) {
533 // IC dispatchers rely on these assumptions to be held.
534 STATIC_ASSERT(FixedArray::kLengthOffset ==
535 StoreHandler::kTransitionCellOffset);
536 DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex),
537 StoreHandler::kSmiHandlerOffset);
538 DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex),
539 StoreHandler::kValidityCellOffset);
540
541 // Both FixedArray and Tuple3 handlers have validity cell at the same offset.
542 Label validity_cell_check_done(this);
543 Node* validity_cell =
544 LoadObjectField(handler, StoreHandler::kValidityCellOffset);
545 GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
546 &validity_cell_check_done);
547 Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
548 GotoIf(WordNotEqual(cell_value,
549 SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
550 miss);
551 Goto(&validity_cell_check_done);
552
553 Bind(&validity_cell_check_done);
554 Node* smi_handler = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
555 CSA_ASSERT(this, TaggedIsSmi(smi_handler));
556
557 Node* maybe_transition_cell =
558 LoadObjectField(handler, StoreHandler::kTransitionCellOffset);
559 Label array_handler(this), tuple_handler(this);
560 Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler);
561
562 Variable var_transition(this, MachineRepresentation::kTagged);
563 Label if_transition(this), if_transition_to_constant(this);
564 Bind(&tuple_handler);
565 {
566 Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
567 var_transition.Bind(transition);
568 Goto(&if_transition);
569 }
570
571 Bind(&array_handler);
572 {
573 Node* length = SmiUntag(maybe_transition_cell);
574 BuildFastLoop(IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length,
575 [this, p, handler, miss](Node* current) {
576 Node* prototype_cell =
577 LoadFixedArrayElement(handler, current);
578 CheckPrototype(prototype_cell, p->name, miss);
579 },
580 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
581
582 Node* maybe_transition_cell =
583 LoadFixedArrayElement(handler, StoreHandler::kTransitionCellIndex);
584 Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
585 var_transition.Bind(transition);
586 Goto(&if_transition);
587 }
588
589 Bind(&if_transition);
590 {
591 Node* holder = p->receiver;
592 Node* transition = var_transition.value();
593 Node* handler_word = SmiUntag(smi_handler);
594
595 GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(transition)), miss);
596
597 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
598 GotoIf(WordEqual(handler_kind,
599 IntPtrConstant(StoreHandler::kTransitionToConstant)),
600 &if_transition_to_constant);
601
602 // Handle transitioning field stores.
603 HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition,
604 miss);
605
606 Bind(&if_transition_to_constant);
607 {
608 // Check that constant matches value.
609 Node* value_index_in_descriptor =
610 DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
611 Node* descriptors = LoadMapDescriptors(transition);
612 Node* constant =
613 LoadFixedArrayElement(descriptors, value_index_in_descriptor);
614 GotoIf(WordNotEqual(p->value, constant), miss);
615
616 StoreMap(p->receiver, transition);
617 Return(p->value);
618 }
619 }
620 }
621
HandleStoreICSmiHandlerCase(Node * handler_word,Node * holder,Node * value,Node * transition,Label * miss)622 void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
623 Node* holder, Node* value,
624 Node* transition,
625 Label* miss) {
626 Comment(transition ? "transitioning field store" : "field store");
627
628 #ifdef DEBUG
629 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
630 if (transition) {
631 CSA_ASSERT(
632 this,
633 Word32Or(
634 WordEqual(handler_kind,
635 IntPtrConstant(StoreHandler::kTransitionToField)),
636 WordEqual(handler_kind,
637 IntPtrConstant(StoreHandler::kTransitionToConstant))));
638 } else {
639 if (FLAG_track_constant_fields) {
640 CSA_ASSERT(
641 this,
642 Word32Or(WordEqual(handler_kind,
643 IntPtrConstant(StoreHandler::kStoreField)),
644 WordEqual(handler_kind,
645 IntPtrConstant(StoreHandler::kStoreConstField))));
646 } else {
647 CSA_ASSERT(this, WordEqual(handler_kind,
648 IntPtrConstant(StoreHandler::kStoreField)));
649 }
650 }
651 #endif
652
653 Node* field_representation =
654 DecodeWord<StoreHandler::FieldRepresentationBits>(handler_word);
655
656 Label if_smi_field(this), if_double_field(this), if_heap_object_field(this),
657 if_tagged_field(this);
658
659 GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kTagged)),
660 &if_tagged_field);
661 GotoIf(WordEqual(field_representation,
662 IntPtrConstant(StoreHandler::kHeapObject)),
663 &if_heap_object_field);
664 GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kDouble)),
665 &if_double_field);
666 CSA_ASSERT(this, WordEqual(field_representation,
667 IntPtrConstant(StoreHandler::kSmi)));
668 Goto(&if_smi_field);
669
670 Bind(&if_tagged_field);
671 {
672 Comment("store tagged field");
673 HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
674 value, transition, miss);
675 }
676
677 Bind(&if_double_field);
678 {
679 Comment("store double field");
680 HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
681 value, transition, miss);
682 }
683
684 Bind(&if_heap_object_field);
685 {
686 Comment("store heap object field");
687 HandleStoreFieldAndReturn(handler_word, holder,
688 Representation::HeapObject(), value, transition,
689 miss);
690 }
691
692 Bind(&if_smi_field);
693 {
694 Comment("store smi field");
695 HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
696 value, transition, miss);
697 }
698 }
699
HandleStoreFieldAndReturn(Node * handler_word,Node * holder,Representation representation,Node * value,Node * transition,Label * miss)700 void AccessorAssembler::HandleStoreFieldAndReturn(Node* handler_word,
701 Node* holder,
702 Representation representation,
703 Node* value, Node* transition,
704 Label* miss) {
705 bool transition_to_field = transition != nullptr;
706 Node* prepared_value = PrepareValueForStore(
707 handler_word, holder, representation, transition, value, miss);
708
709 Label if_inobject(this), if_out_of_object(this);
710 Branch(IsSetWord<StoreHandler::IsInobjectBits>(handler_word), &if_inobject,
711 &if_out_of_object);
712
713 Bind(&if_inobject);
714 {
715 StoreNamedField(handler_word, holder, true, representation, prepared_value,
716 transition_to_field, miss);
717 if (transition_to_field) {
718 StoreMap(holder, transition);
719 }
720 Return(value);
721 }
722
723 Bind(&if_out_of_object);
724 {
725 if (transition_to_field) {
726 Label storage_extended(this);
727 GotoIfNot(IsSetWord<StoreHandler::ExtendStorageBits>(handler_word),
728 &storage_extended);
729 Comment("[ Extend storage");
730 ExtendPropertiesBackingStore(holder);
731 Comment("] Extend storage");
732 Goto(&storage_extended);
733
734 Bind(&storage_extended);
735 }
736
737 StoreNamedField(handler_word, holder, false, representation, prepared_value,
738 transition_to_field, miss);
739 if (transition_to_field) {
740 StoreMap(holder, transition);
741 }
742 Return(value);
743 }
744 }
745
PrepareValueForStore(Node * handler_word,Node * holder,Representation representation,Node * transition,Node * value,Label * bailout)746 Node* AccessorAssembler::PrepareValueForStore(Node* handler_word, Node* holder,
747 Representation representation,
748 Node* transition, Node* value,
749 Label* bailout) {
750 if (representation.IsDouble()) {
751 value = TryTaggedToFloat64(value, bailout);
752
753 } else if (representation.IsHeapObject()) {
754 GotoIf(TaggedIsSmi(value), bailout);
755
756 Label done(this);
757 if (FLAG_track_constant_fields && !transition) {
758 // Skip field type check in favor of constant value check when storing
759 // to constant field.
760 GotoIf(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
761 IntPtrConstant(StoreHandler::kStoreConstField)),
762 &done);
763 }
764 Node* value_index_in_descriptor =
765 DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
766 Node* descriptors =
767 LoadMapDescriptors(transition ? transition : LoadMap(holder));
768 Node* maybe_field_type =
769 LoadFixedArrayElement(descriptors, value_index_in_descriptor);
770
771 GotoIf(TaggedIsSmi(maybe_field_type), &done);
772 // Check that value type matches the field type.
773 {
774 Node* field_type = LoadWeakCellValue(maybe_field_type, bailout);
775 Branch(WordEqual(LoadMap(value), field_type), &done, bailout);
776 }
777 Bind(&done);
778
779 } else if (representation.IsSmi()) {
780 GotoIfNot(TaggedIsSmi(value), bailout);
781
782 } else {
783 DCHECK(representation.IsTagged());
784 }
785 return value;
786 }
787
ExtendPropertiesBackingStore(Node * object)788 void AccessorAssembler::ExtendPropertiesBackingStore(Node* object) {
789 Node* properties = LoadProperties(object);
790 Node* length = LoadFixedArrayBaseLength(properties);
791
792 ParameterMode mode = OptimalParameterMode();
793 length = TaggedToParameter(length, mode);
794
795 Node* delta = IntPtrOrSmiConstant(JSObject::kFieldsAdded, mode);
796 Node* new_capacity = IntPtrOrSmiAdd(length, delta, mode);
797
798 // Grow properties array.
799 ElementsKind kind = FAST_ELEMENTS;
800 DCHECK(kMaxNumberOfDescriptors + JSObject::kFieldsAdded <
801 FixedArrayBase::GetMaxLengthForNewSpaceAllocation(kind));
802 // The size of a new properties backing store is guaranteed to be small
803 // enough that the new backing store will be allocated in new space.
804 CSA_ASSERT(this,
805 UintPtrOrSmiLessThan(
806 new_capacity,
807 IntPtrOrSmiConstant(
808 kMaxNumberOfDescriptors + JSObject::kFieldsAdded, mode),
809 mode));
810
811 Node* new_properties = AllocateFixedArray(kind, new_capacity, mode);
812
813 FillFixedArrayWithValue(kind, new_properties, length, new_capacity,
814 Heap::kUndefinedValueRootIndex, mode);
815
816 // |new_properties| is guaranteed to be in new space, so we can skip
817 // the write barrier.
818 CopyFixedArrayElements(kind, properties, new_properties, length,
819 SKIP_WRITE_BARRIER, mode);
820
821 StoreObjectField(object, JSObject::kPropertiesOffset, new_properties);
822 }
823
StoreNamedField(Node * handler_word,Node * object,bool is_inobject,Representation representation,Node * value,bool transition_to_field,Label * bailout)824 void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object,
825 bool is_inobject,
826 Representation representation,
827 Node* value, bool transition_to_field,
828 Label* bailout) {
829 bool store_value_as_double = representation.IsDouble();
830 Node* property_storage = object;
831 if (!is_inobject) {
832 property_storage = LoadProperties(object);
833 }
834
835 Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
836 if (representation.IsDouble()) {
837 if (!FLAG_unbox_double_fields || !is_inobject) {
838 if (transition_to_field) {
839 Node* heap_number = AllocateHeapNumberWithValue(value, MUTABLE);
840 // Store the new mutable heap number into the object.
841 value = heap_number;
842 store_value_as_double = false;
843 } else {
844 // Load the heap number.
845 property_storage = LoadObjectField(property_storage, offset);
846 // Store the double value into it.
847 offset = IntPtrConstant(HeapNumber::kValueOffset);
848 }
849 }
850 }
851
852 // Do constant value check if necessary.
853 if (FLAG_track_constant_fields && !transition_to_field) {
854 Label done(this);
855 GotoIfNot(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
856 IntPtrConstant(StoreHandler::kStoreConstField)),
857 &done);
858 {
859 if (store_value_as_double) {
860 Node* current_value =
861 LoadObjectField(property_storage, offset, MachineType::Float64());
862 GotoIfNot(Float64Equal(current_value, value), bailout);
863 } else {
864 Node* current_value = LoadObjectField(property_storage, offset);
865 GotoIfNot(WordEqual(current_value, value), bailout);
866 }
867 Goto(&done);
868 }
869 Bind(&done);
870 }
871
872 // Do the store.
873 if (store_value_as_double) {
874 StoreObjectFieldNoWriteBarrier(property_storage, offset, value,
875 MachineRepresentation::kFloat64);
876 } else if (representation.IsSmi()) {
877 StoreObjectFieldNoWriteBarrier(property_storage, offset, value);
878 } else {
879 StoreObjectField(property_storage, offset, value);
880 }
881 }
882
EmitFastElementsBoundsCheck(Node * object,Node * elements,Node * intptr_index,Node * is_jsarray_condition,Label * miss)883 void AccessorAssembler::EmitFastElementsBoundsCheck(Node* object,
884 Node* elements,
885 Node* intptr_index,
886 Node* is_jsarray_condition,
887 Label* miss) {
888 Variable var_length(this, MachineType::PointerRepresentation());
889 Comment("Fast elements bounds check");
890 Label if_array(this), length_loaded(this, &var_length);
891 GotoIf(is_jsarray_condition, &if_array);
892 {
893 var_length.Bind(SmiUntag(LoadFixedArrayBaseLength(elements)));
894 Goto(&length_loaded);
895 }
896 Bind(&if_array);
897 {
898 var_length.Bind(SmiUntag(LoadJSArrayLength(object)));
899 Goto(&length_loaded);
900 }
901 Bind(&length_loaded);
902 GotoIfNot(UintPtrLessThan(intptr_index, var_length.value()), miss);
903 }
904
EmitElementLoad(Node * object,Node * elements,Node * elements_kind,Node * intptr_index,Node * is_jsarray_condition,Label * if_hole,Label * rebox_double,Variable * var_double_value,Label * unimplemented_elements_kind,Label * out_of_bounds,Label * miss,ExitPoint * exit_point)905 void AccessorAssembler::EmitElementLoad(
906 Node* object, Node* elements, Node* elements_kind, Node* intptr_index,
907 Node* is_jsarray_condition, Label* if_hole, Label* rebox_double,
908 Variable* var_double_value, Label* unimplemented_elements_kind,
909 Label* out_of_bounds, Label* miss, ExitPoint* exit_point) {
910 Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this),
911 if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
912 if_dictionary(this);
913 GotoIf(
914 Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
915 &if_nonfast);
916
917 EmitFastElementsBoundsCheck(object, elements, intptr_index,
918 is_jsarray_condition, out_of_bounds);
919 int32_t kinds[] = {// Handled by if_fast_packed.
920 FAST_SMI_ELEMENTS, FAST_ELEMENTS,
921 // Handled by if_fast_holey.
922 FAST_HOLEY_SMI_ELEMENTS, FAST_HOLEY_ELEMENTS,
923 // Handled by if_fast_double.
924 FAST_DOUBLE_ELEMENTS,
925 // Handled by if_fast_holey_double.
926 FAST_HOLEY_DOUBLE_ELEMENTS};
927 Label* labels[] = {// FAST_{SMI,}_ELEMENTS
928 &if_fast_packed, &if_fast_packed,
929 // FAST_HOLEY_{SMI,}_ELEMENTS
930 &if_fast_holey, &if_fast_holey,
931 // FAST_DOUBLE_ELEMENTS
932 &if_fast_double,
933 // FAST_HOLEY_DOUBLE_ELEMENTS
934 &if_fast_holey_double};
935 Switch(elements_kind, unimplemented_elements_kind, kinds, labels,
936 arraysize(kinds));
937
938 Bind(&if_fast_packed);
939 {
940 Comment("fast packed elements");
941 exit_point->Return(LoadFixedArrayElement(elements, intptr_index));
942 }
943
944 Bind(&if_fast_holey);
945 {
946 Comment("fast holey elements");
947 Node* element = LoadFixedArrayElement(elements, intptr_index);
948 GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
949 exit_point->Return(element);
950 }
951
952 Bind(&if_fast_double);
953 {
954 Comment("packed double elements");
955 var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index,
956 MachineType::Float64()));
957 Goto(rebox_double);
958 }
959
960 Bind(&if_fast_holey_double);
961 {
962 Comment("holey double elements");
963 Node* value = LoadFixedDoubleArrayElement(elements, intptr_index,
964 MachineType::Float64(), 0,
965 INTPTR_PARAMETERS, if_hole);
966 var_double_value->Bind(value);
967 Goto(rebox_double);
968 }
969
970 Bind(&if_nonfast);
971 {
972 STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
973 GotoIf(Int32GreaterThanOrEqual(
974 elements_kind,
975 Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
976 &if_typed_array);
977 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
978 &if_dictionary);
979 Goto(unimplemented_elements_kind);
980 }
981
982 Bind(&if_dictionary);
983 {
984 Comment("dictionary elements");
985 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
986 Variable var_entry(this, MachineType::PointerRepresentation());
987 Label if_found(this);
988 NumberDictionaryLookup<SeededNumberDictionary>(
989 elements, intptr_index, &if_found, &var_entry, if_hole);
990 Bind(&if_found);
991 // Check that the value is a data property.
992 Node* index = EntryToIndex<SeededNumberDictionary>(var_entry.value());
993 Node* details =
994 LoadDetailsByKeyIndex<SeededNumberDictionary>(elements, index);
995 Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
996 // TODO(jkummerow): Support accessors without missing?
997 GotoIfNot(Word32Equal(kind, Int32Constant(kData)), miss);
998 // Finally, load the value.
999 exit_point->Return(
1000 LoadValueByKeyIndex<SeededNumberDictionary>(elements, index));
1001 }
1002
1003 Bind(&if_typed_array);
1004 {
1005 Comment("typed elements");
1006 // Check if buffer has been neutered.
1007 Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
1008 GotoIf(IsDetachedBuffer(buffer), miss);
1009
1010 // Bounds check.
1011 Node* length =
1012 SmiUntag(LoadObjectField(object, JSTypedArray::kLengthOffset));
1013 GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds);
1014
1015 // Backing store = external_pointer + base_pointer.
1016 Node* external_pointer =
1017 LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
1018 MachineType::Pointer());
1019 Node* base_pointer =
1020 LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
1021 Node* backing_store =
1022 IntPtrAdd(external_pointer, BitcastTaggedToWord(base_pointer));
1023
1024 Label uint8_elements(this), int8_elements(this), uint16_elements(this),
1025 int16_elements(this), uint32_elements(this), int32_elements(this),
1026 float32_elements(this), float64_elements(this);
1027 Label* elements_kind_labels[] = {
1028 &uint8_elements, &uint8_elements, &int8_elements,
1029 &uint16_elements, &int16_elements, &uint32_elements,
1030 &int32_elements, &float32_elements, &float64_elements};
1031 int32_t elements_kinds[] = {
1032 UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
1033 UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
1034 INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
1035 const size_t kTypedElementsKindCount =
1036 LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
1037 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
1038 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
1039 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
1040 Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
1041 kTypedElementsKindCount);
1042 Bind(&uint8_elements);
1043 {
1044 Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
1045 Node* element = Load(MachineType::Uint8(), backing_store, intptr_index);
1046 exit_point->Return(SmiFromWord32(element));
1047 }
1048 Bind(&int8_elements);
1049 {
1050 Comment("INT8_ELEMENTS");
1051 Node* element = Load(MachineType::Int8(), backing_store, intptr_index);
1052 exit_point->Return(SmiFromWord32(element));
1053 }
1054 Bind(&uint16_elements);
1055 {
1056 Comment("UINT16_ELEMENTS");
1057 Node* index = WordShl(intptr_index, IntPtrConstant(1));
1058 Node* element = Load(MachineType::Uint16(), backing_store, index);
1059 exit_point->Return(SmiFromWord32(element));
1060 }
1061 Bind(&int16_elements);
1062 {
1063 Comment("INT16_ELEMENTS");
1064 Node* index = WordShl(intptr_index, IntPtrConstant(1));
1065 Node* element = Load(MachineType::Int16(), backing_store, index);
1066 exit_point->Return(SmiFromWord32(element));
1067 }
1068 Bind(&uint32_elements);
1069 {
1070 Comment("UINT32_ELEMENTS");
1071 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1072 Node* element = Load(MachineType::Uint32(), backing_store, index);
1073 exit_point->Return(ChangeUint32ToTagged(element));
1074 }
1075 Bind(&int32_elements);
1076 {
1077 Comment("INT32_ELEMENTS");
1078 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1079 Node* element = Load(MachineType::Int32(), backing_store, index);
1080 exit_point->Return(ChangeInt32ToTagged(element));
1081 }
1082 Bind(&float32_elements);
1083 {
1084 Comment("FLOAT32_ELEMENTS");
1085 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1086 Node* element = Load(MachineType::Float32(), backing_store, index);
1087 var_double_value->Bind(ChangeFloat32ToFloat64(element));
1088 Goto(rebox_double);
1089 }
1090 Bind(&float64_elements);
1091 {
1092 Comment("FLOAT64_ELEMENTS");
1093 Node* index = WordShl(intptr_index, IntPtrConstant(3));
1094 Node* element = Load(MachineType::Float64(), backing_store, index);
1095 var_double_value->Bind(element);
1096 Goto(rebox_double);
1097 }
1098 }
1099 }
1100
CheckPrototype(Node * prototype_cell,Node * name,Label * miss)1101 void AccessorAssembler::CheckPrototype(Node* prototype_cell, Node* name,
1102 Label* miss) {
1103 Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
1104
1105 Label done(this);
1106 Label if_property_cell(this), if_dictionary_object(this);
1107
1108 // |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
1109 Branch(WordEqual(LoadMap(maybe_prototype),
1110 LoadRoot(Heap::kGlobalPropertyCellMapRootIndex)),
1111 &if_property_cell, &if_dictionary_object);
1112
1113 Bind(&if_dictionary_object);
1114 {
1115 CSA_ASSERT(this, IsDictionaryMap(LoadMap(maybe_prototype)));
1116 NameDictionaryNegativeLookup(maybe_prototype, name, miss);
1117 Goto(&done);
1118 }
1119
1120 Bind(&if_property_cell);
1121 {
1122 // Ensure the property cell still contains the hole.
1123 Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
1124 GotoIf(WordNotEqual(value, LoadRoot(Heap::kTheHoleValueRootIndex)), miss);
1125 Goto(&done);
1126 }
1127
1128 Bind(&done);
1129 }
1130
NameDictionaryNegativeLookup(Node * object,Node * name,Label * miss)1131 void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, Node* name,
1132 Label* miss) {
1133 CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
1134 Node* properties = LoadProperties(object);
1135 // Ensure the property does not exist in a dictionary-mode object.
1136 Variable var_name_index(this, MachineType::PointerRepresentation());
1137 Label done(this);
1138 NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index,
1139 &done);
1140 Bind(&done);
1141 }
1142
GenericElementLoad(Node * receiver,Node * receiver_map,Node * instance_type,Node * index,Label * slow)1143 void AccessorAssembler::GenericElementLoad(Node* receiver, Node* receiver_map,
1144 Node* instance_type, Node* index,
1145 Label* slow) {
1146 Comment("integer index");
1147
1148 ExitPoint direct_exit(this);
1149
1150 Label if_element_hole(this), if_oob(this);
1151 // Receivers requiring non-standard element accesses (interceptors, access
1152 // checks, strings and string wrappers, proxies) are handled in the runtime.
1153 GotoIf(Int32LessThanOrEqual(instance_type,
1154 Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
1155 slow);
1156 Node* elements = LoadElements(receiver);
1157 Node* elements_kind = LoadMapElementsKind(receiver_map);
1158 Node* is_jsarray_condition =
1159 Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE));
1160 Variable var_double_value(this, MachineRepresentation::kFloat64);
1161 Label rebox_double(this, &var_double_value);
1162
1163 // Unimplemented elements kinds fall back to a runtime call.
1164 Label* unimplemented_elements_kind = slow;
1165 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
1166 EmitElementLoad(receiver, elements, elements_kind, index,
1167 is_jsarray_condition, &if_element_hole, &rebox_double,
1168 &var_double_value, unimplemented_elements_kind, &if_oob, slow,
1169 &direct_exit);
1170
1171 Bind(&rebox_double);
1172 Return(AllocateHeapNumberWithValue(var_double_value.value()));
1173
1174 Bind(&if_oob);
1175 {
1176 Comment("out of bounds");
1177 // Negative keys can't take the fast OOB path.
1178 GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), slow);
1179 // Positive OOB indices are effectively the same as hole loads.
1180 Goto(&if_element_hole);
1181 }
1182
1183 Bind(&if_element_hole);
1184 {
1185 Comment("found the hole");
1186 Label return_undefined(this);
1187 BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, slow);
1188
1189 Bind(&return_undefined);
1190 Return(UndefinedConstant());
1191 }
1192 }
1193
GenericPropertyLoad(Node * receiver,Node * receiver_map,Node * instance_type,Node * key,const LoadICParameters * p,Label * slow)1194 void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
1195 Node* instance_type, Node* key,
1196 const LoadICParameters* p,
1197 Label* slow) {
1198 Comment("key is unique name");
1199 Label if_found_on_receiver(this), if_property_dictionary(this),
1200 lookup_prototype_chain(this);
1201 Variable var_details(this, MachineRepresentation::kWord32);
1202 Variable var_value(this, MachineRepresentation::kTagged);
1203
1204 // Receivers requiring non-standard accesses (interceptors, access
1205 // checks, strings and string wrappers, proxies) are handled in the runtime.
1206 GotoIf(Int32LessThanOrEqual(instance_type,
1207 Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
1208 slow);
1209
1210 // Check if the receiver has fast or slow properties.
1211 Node* properties = LoadProperties(receiver);
1212 Node* properties_map = LoadMap(properties);
1213 GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
1214 &if_property_dictionary);
1215
1216 // Try looking up the property on the receiver; if unsuccessful, look
1217 // for a handler in the stub cache.
1218 Node* bitfield3 = LoadMapBitField3(receiver_map);
1219 Node* descriptors = LoadMapDescriptors(receiver_map);
1220
1221 Label if_descriptor_found(this), stub_cache(this);
1222 Variable var_name_index(this, MachineType::PointerRepresentation());
1223 DescriptorLookup(key, descriptors, bitfield3, &if_descriptor_found,
1224 &var_name_index, &stub_cache);
1225
1226 Bind(&if_descriptor_found);
1227 {
1228 LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
1229 var_name_index.value(), &var_details,
1230 &var_value);
1231 Goto(&if_found_on_receiver);
1232 }
1233
1234 Bind(&stub_cache);
1235 {
1236 Comment("stub cache probe for fast property load");
1237 Variable var_handler(this, MachineRepresentation::kTagged);
1238 Label found_handler(this, &var_handler), stub_cache_miss(this);
1239 TryProbeStubCache(isolate()->load_stub_cache(), receiver, key,
1240 &found_handler, &var_handler, &stub_cache_miss);
1241 Bind(&found_handler);
1242 { HandleLoadICHandlerCase(p, var_handler.value(), slow); }
1243
1244 Bind(&stub_cache_miss);
1245 {
1246 // TODO(jkummerow): Check if the property exists on the prototype
1247 // chain. If it doesn't, then there's no point in missing.
1248 Comment("KeyedLoadGeneric_miss");
1249 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
1250 p->name, p->slot, p->vector);
1251 }
1252 }
1253
1254 Bind(&if_property_dictionary);
1255 {
1256 Comment("dictionary property load");
1257 // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
1258 // seeing global objects here (which would need special handling).
1259
1260 Variable var_name_index(this, MachineType::PointerRepresentation());
1261 Label dictionary_found(this, &var_name_index);
1262 NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,
1263 &var_name_index,
1264 &lookup_prototype_chain);
1265 Bind(&dictionary_found);
1266 {
1267 LoadPropertyFromNameDictionary(properties, var_name_index.value(),
1268 &var_details, &var_value);
1269 Goto(&if_found_on_receiver);
1270 }
1271 }
1272
1273 Bind(&if_found_on_receiver);
1274 {
1275 Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
1276 p->context, receiver, slow);
1277 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1);
1278 Return(value);
1279 }
1280
1281 Bind(&lookup_prototype_chain);
1282 {
1283 Variable var_holder_map(this, MachineRepresentation::kTagged);
1284 Variable var_holder_instance_type(this, MachineRepresentation::kWord32);
1285 Label return_undefined(this);
1286 Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type};
1287 Label loop(this, arraysize(merged_variables), merged_variables);
1288
1289 var_holder_map.Bind(receiver_map);
1290 var_holder_instance_type.Bind(instance_type);
1291 // Private symbols must not be looked up on the prototype chain.
1292 GotoIf(IsPrivateSymbol(key), &return_undefined);
1293 Goto(&loop);
1294 Bind(&loop);
1295 {
1296 // Bailout if it can be an integer indexed exotic case.
1297 GotoIf(Word32Equal(var_holder_instance_type.value(),
1298 Int32Constant(JS_TYPED_ARRAY_TYPE)),
1299 slow);
1300 Node* proto = LoadMapPrototype(var_holder_map.value());
1301 GotoIf(WordEqual(proto, NullConstant()), &return_undefined);
1302 Node* proto_map = LoadMap(proto);
1303 Node* proto_instance_type = LoadMapInstanceType(proto_map);
1304 var_holder_map.Bind(proto_map);
1305 var_holder_instance_type.Bind(proto_instance_type);
1306 Label next_proto(this), return_value(this, &var_value), goto_slow(this);
1307 TryGetOwnProperty(p->context, receiver, proto, proto_map,
1308 proto_instance_type, key, &return_value, &var_value,
1309 &next_proto, &goto_slow);
1310
1311 // This trampoline and the next are required to appease Turbofan's
1312 // variable merging.
1313 Bind(&next_proto);
1314 Goto(&loop);
1315
1316 Bind(&goto_slow);
1317 Goto(slow);
1318
1319 Bind(&return_value);
1320 Return(var_value.value());
1321 }
1322
1323 Bind(&return_undefined);
1324 Return(UndefinedConstant());
1325 }
1326 }
1327
1328 //////////////////// Stub cache access helpers.
1329
1330 enum AccessorAssembler::StubCacheTable : int {
1331 kPrimary = static_cast<int>(StubCache::kPrimary),
1332 kSecondary = static_cast<int>(StubCache::kSecondary)
1333 };
1334
StubCachePrimaryOffset(Node * name,Node * map)1335 Node* AccessorAssembler::StubCachePrimaryOffset(Node* name, Node* map) {
1336 // See v8::internal::StubCache::PrimaryOffset().
1337 STATIC_ASSERT(StubCache::kCacheIndexShift == Name::kHashShift);
1338 // Compute the hash of the name (use entire hash field).
1339 Node* hash_field = LoadNameHashField(name);
1340 CSA_ASSERT(this,
1341 Word32Equal(Word32And(hash_field,
1342 Int32Constant(Name::kHashNotComputedMask)),
1343 Int32Constant(0)));
1344
1345 // Using only the low bits in 64-bit mode is unlikely to increase the
1346 // risk of collision even if the heap is spread over an area larger than
1347 // 4Gb (and not at all if it isn't).
1348 Node* map32 = TruncateWordToWord32(BitcastTaggedToWord(map));
1349 Node* hash = Int32Add(hash_field, map32);
1350 // Base the offset on a simple combination of name and map.
1351 hash = Word32Xor(hash, Int32Constant(StubCache::kPrimaryMagic));
1352 uint32_t mask = (StubCache::kPrimaryTableSize - 1)
1353 << StubCache::kCacheIndexShift;
1354 return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
1355 }
1356
StubCacheSecondaryOffset(Node * name,Node * seed)1357 Node* AccessorAssembler::StubCacheSecondaryOffset(Node* name, Node* seed) {
1358 // See v8::internal::StubCache::SecondaryOffset().
1359
1360 // Use the seed from the primary cache in the secondary cache.
1361 Node* name32 = TruncateWordToWord32(BitcastTaggedToWord(name));
1362 Node* hash = Int32Sub(TruncateWordToWord32(seed), name32);
1363 hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
1364 int32_t mask = (StubCache::kSecondaryTableSize - 1)
1365 << StubCache::kCacheIndexShift;
1366 return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
1367 }
1368
TryProbeStubCacheTable(StubCache * stub_cache,StubCacheTable table_id,Node * entry_offset,Node * name,Node * map,Label * if_handler,Variable * var_handler,Label * if_miss)1369 void AccessorAssembler::TryProbeStubCacheTable(StubCache* stub_cache,
1370 StubCacheTable table_id,
1371 Node* entry_offset, Node* name,
1372 Node* map, Label* if_handler,
1373 Variable* var_handler,
1374 Label* if_miss) {
1375 StubCache::Table table = static_cast<StubCache::Table>(table_id);
1376 #ifdef DEBUG
1377 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
1378 Goto(if_miss);
1379 return;
1380 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
1381 Goto(if_miss);
1382 return;
1383 }
1384 #endif
1385 // The {table_offset} holds the entry offset times four (due to masking
1386 // and shifting optimizations).
1387 const int kMultiplier = sizeof(StubCache::Entry) >> Name::kHashShift;
1388 entry_offset = IntPtrMul(entry_offset, IntPtrConstant(kMultiplier));
1389
1390 // Check that the key in the entry matches the name.
1391 Node* key_base =
1392 ExternalConstant(ExternalReference(stub_cache->key_reference(table)));
1393 Node* entry_key = Load(MachineType::Pointer(), key_base, entry_offset);
1394 GotoIf(WordNotEqual(name, entry_key), if_miss);
1395
1396 // Get the map entry from the cache.
1397 DCHECK_EQ(kPointerSize * 2, stub_cache->map_reference(table).address() -
1398 stub_cache->key_reference(table).address());
1399 Node* entry_map =
1400 Load(MachineType::Pointer(), key_base,
1401 IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize * 2)));
1402 GotoIf(WordNotEqual(map, entry_map), if_miss);
1403
1404 DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() -
1405 stub_cache->key_reference(table).address());
1406 Node* handler = Load(MachineType::TaggedPointer(), key_base,
1407 IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
1408
1409 // We found the handler.
1410 var_handler->Bind(handler);
1411 Goto(if_handler);
1412 }
1413
TryProbeStubCache(StubCache * stub_cache,Node * receiver,Node * name,Label * if_handler,Variable * var_handler,Label * if_miss)1414 void AccessorAssembler::TryProbeStubCache(StubCache* stub_cache, Node* receiver,
1415 Node* name, Label* if_handler,
1416 Variable* var_handler,
1417 Label* if_miss) {
1418 Label try_secondary(this), miss(this);
1419
1420 Counters* counters = isolate()->counters();
1421 IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
1422
1423 // Check that the {receiver} isn't a smi.
1424 GotoIf(TaggedIsSmi(receiver), &miss);
1425
1426 Node* receiver_map = LoadMap(receiver);
1427
1428 // Probe the primary table.
1429 Node* primary_offset = StubCachePrimaryOffset(name, receiver_map);
1430 TryProbeStubCacheTable(stub_cache, kPrimary, primary_offset, name,
1431 receiver_map, if_handler, var_handler, &try_secondary);
1432
1433 Bind(&try_secondary);
1434 {
1435 // Probe the secondary table.
1436 Node* secondary_offset = StubCacheSecondaryOffset(name, primary_offset);
1437 TryProbeStubCacheTable(stub_cache, kSecondary, secondary_offset, name,
1438 receiver_map, if_handler, var_handler, &miss);
1439 }
1440
1441 Bind(&miss);
1442 {
1443 IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
1444 Goto(if_miss);
1445 }
1446 }
1447
1448 //////////////////// Entry points into private implementation (one per stub).
1449
LoadIC(const LoadICParameters * p)1450 void AccessorAssembler::LoadIC(const LoadICParameters* p) {
1451 Variable var_handler(this, MachineRepresentation::kTagged);
1452 // TODO(ishell): defer blocks when it works.
1453 Label if_handler(this, &var_handler), try_polymorphic(this),
1454 try_megamorphic(this /*, Label::kDeferred*/),
1455 miss(this /*, Label::kDeferred*/);
1456
1457 Node* receiver_map = LoadReceiverMap(p->receiver);
1458 GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(receiver_map)), &miss);
1459
1460 // Check monomorphic case.
1461 Node* feedback =
1462 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
1463 &var_handler, &try_polymorphic);
1464 Bind(&if_handler);
1465 { HandleLoadICHandlerCase(p, var_handler.value(), &miss); }
1466
1467 Bind(&try_polymorphic);
1468 {
1469 // Check polymorphic case.
1470 Comment("LoadIC_try_polymorphic");
1471 GotoIfNot(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
1472 &try_megamorphic);
1473 HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
1474 &miss, 2);
1475 }
1476
1477 Bind(&try_megamorphic);
1478 {
1479 // Check megamorphic case.
1480 GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
1481 &miss);
1482
1483 TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name,
1484 &if_handler, &var_handler, &miss);
1485 }
1486 Bind(&miss);
1487 {
1488 TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
1489 p->slot, p->vector);
1490 }
1491 }
1492
LoadICProtoArray(const LoadICParameters * p,Node * handler,bool throw_reference_error_if_nonexistent)1493 void AccessorAssembler::LoadICProtoArray(
1494 const LoadICParameters* p, Node* handler,
1495 bool throw_reference_error_if_nonexistent) {
1496 Label miss(this);
1497 CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
1498 CSA_ASSERT(this, IsFixedArrayMap(LoadMap(handler)));
1499
1500 ExitPoint direct_exit(this);
1501
1502 Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
1503 Node* handler_flags = SmiUntag(smi_handler);
1504
1505 Node* handler_length = LoadAndUntagFixedArrayBaseLength(handler);
1506
1507 Node* holder =
1508 EmitLoadICProtoArrayCheck(p, handler, handler_length, handler_flags,
1509 &miss, throw_reference_error_if_nonexistent);
1510
1511 HandleLoadICSmiHandlerCase(p, holder, smi_handler, &miss, &direct_exit,
1512 kOnlyProperties);
1513
1514 Bind(&miss);
1515 {
1516 TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
1517 p->slot, p->vector);
1518 }
1519 }
1520
LoadGlobalIC_TryPropertyCellCase(Node * vector,Node * slot,ExitPoint * exit_point,Label * try_handler,Label * miss,ParameterMode slot_mode)1521 void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase(
1522 Node* vector, Node* slot, ExitPoint* exit_point, Label* try_handler,
1523 Label* miss, ParameterMode slot_mode) {
1524 Comment("LoadGlobalIC_TryPropertyCellCase");
1525
1526 Node* weak_cell = LoadFixedArrayElement(vector, slot, 0, slot_mode);
1527 CSA_ASSERT(this, HasInstanceType(weak_cell, WEAK_CELL_TYPE));
1528
1529 // Load value or try handler case if the {weak_cell} is cleared.
1530 Node* property_cell = LoadWeakCellValue(weak_cell, try_handler);
1531 CSA_ASSERT(this, HasInstanceType(property_cell, PROPERTY_CELL_TYPE));
1532
1533 Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset);
1534 GotoIf(WordEqual(value, TheHoleConstant()), miss);
1535 exit_point->Return(value);
1536 }
1537
LoadGlobalIC_TryHandlerCase(const LoadICParameters * p,TypeofMode typeof_mode,ExitPoint * exit_point,Label * miss)1538 void AccessorAssembler::LoadGlobalIC_TryHandlerCase(const LoadICParameters* p,
1539 TypeofMode typeof_mode,
1540 ExitPoint* exit_point,
1541 Label* miss) {
1542 Comment("LoadGlobalIC_TryHandlerCase");
1543
1544 Label call_handler(this);
1545
1546 Node* handler =
1547 LoadFixedArrayElement(p->vector, p->slot, kPointerSize, SMI_PARAMETERS);
1548 CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
1549 GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
1550 miss);
1551 GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
1552
1553 bool throw_reference_error_if_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF;
1554 HandleLoadGlobalICHandlerCase(p, handler, miss, exit_point,
1555 throw_reference_error_if_nonexistent);
1556
1557 Bind(&call_handler);
1558 {
1559 LoadWithVectorDescriptor descriptor(isolate());
1560 Node* native_context = LoadNativeContext(p->context);
1561 Node* receiver =
1562 LoadContextElement(native_context, Context::EXTENSION_INDEX);
1563 exit_point->ReturnCallStub(descriptor, handler, p->context, receiver,
1564 p->name, p->slot, p->vector);
1565 }
1566 }
1567
LoadGlobalIC_MissCase(const LoadICParameters * p,ExitPoint * exit_point)1568 void AccessorAssembler::LoadGlobalIC_MissCase(const LoadICParameters* p,
1569 ExitPoint* exit_point) {
1570 Comment("LoadGlobalIC_MissCase");
1571
1572 exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context,
1573 p->name, p->slot, p->vector);
1574 }
1575
LoadGlobalIC(const LoadICParameters * p,TypeofMode typeof_mode)1576 void AccessorAssembler::LoadGlobalIC(const LoadICParameters* p,
1577 TypeofMode typeof_mode) {
1578 ExitPoint direct_exit(this);
1579
1580 Label try_handler(this), miss(this);
1581 LoadGlobalIC_TryPropertyCellCase(p->vector, p->slot, &direct_exit,
1582 &try_handler, &miss);
1583
1584 Bind(&try_handler);
1585 LoadGlobalIC_TryHandlerCase(p, typeof_mode, &direct_exit, &miss);
1586
1587 Bind(&miss);
1588 LoadGlobalIC_MissCase(p, &direct_exit);
1589 }
1590
KeyedLoadIC(const LoadICParameters * p)1591 void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) {
1592 Variable var_handler(this, MachineRepresentation::kTagged);
1593 // TODO(ishell): defer blocks when it works.
1594 Label if_handler(this, &var_handler), try_polymorphic(this),
1595 try_megamorphic(this /*, Label::kDeferred*/),
1596 try_polymorphic_name(this /*, Label::kDeferred*/),
1597 miss(this /*, Label::kDeferred*/);
1598
1599 Node* receiver_map = LoadReceiverMap(p->receiver);
1600 GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(receiver_map)), &miss);
1601
1602 // Check monomorphic case.
1603 Node* feedback =
1604 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
1605 &var_handler, &try_polymorphic);
1606 Bind(&if_handler);
1607 { HandleLoadICHandlerCase(p, var_handler.value(), &miss, kSupportElements); }
1608
1609 Bind(&try_polymorphic);
1610 {
1611 // Check polymorphic case.
1612 Comment("KeyedLoadIC_try_polymorphic");
1613 GotoIfNot(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
1614 &try_megamorphic);
1615 HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
1616 &miss, 2);
1617 }
1618
1619 Bind(&try_megamorphic);
1620 {
1621 // Check megamorphic case.
1622 Comment("KeyedLoadIC_try_megamorphic");
1623 GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
1624 &try_polymorphic_name);
1625 // TODO(jkummerow): Inline this? Or some of it?
1626 TailCallStub(CodeFactory::KeyedLoadIC_Megamorphic(isolate()), p->context,
1627 p->receiver, p->name, p->slot, p->vector);
1628 }
1629 Bind(&try_polymorphic_name);
1630 {
1631 // We might have a name in feedback, and a fixed array in the next slot.
1632 Comment("KeyedLoadIC_try_polymorphic_name");
1633 GotoIfNot(WordEqual(feedback, p->name), &miss);
1634 // If the name comparison succeeded, we know we have a fixed array with
1635 // at least one map/handler pair.
1636 Node* offset = ElementOffsetFromIndex(
1637 p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
1638 FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
1639 Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
1640 HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss,
1641 1);
1642 }
1643 Bind(&miss);
1644 {
1645 Comment("KeyedLoadIC_miss");
1646 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
1647 p->name, p->slot, p->vector);
1648 }
1649 }
1650
KeyedLoadICGeneric(const LoadICParameters * p)1651 void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
1652 Variable var_index(this, MachineType::PointerRepresentation());
1653 Variable var_unique(this, MachineRepresentation::kTagged);
1654 var_unique.Bind(p->name); // Dummy initialization.
1655 Label if_index(this), if_unique_name(this), slow(this);
1656
1657 Node* receiver = p->receiver;
1658 GotoIf(TaggedIsSmi(receiver), &slow);
1659 Node* receiver_map = LoadMap(receiver);
1660 Node* instance_type = LoadMapInstanceType(receiver_map);
1661
1662 TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
1663 &slow);
1664
1665 Bind(&if_index);
1666 {
1667 GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(),
1668 &slow);
1669 }
1670
1671 Bind(&if_unique_name);
1672 {
1673 GenericPropertyLoad(receiver, receiver_map, instance_type,
1674 var_unique.value(), p, &slow);
1675 }
1676
1677 Bind(&slow);
1678 {
1679 Comment("KeyedLoadGeneric_slow");
1680 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1);
1681 // TODO(jkummerow): Should we use the GetProperty TF stub instead?
1682 TailCallRuntime(Runtime::kKeyedGetProperty, p->context, p->receiver,
1683 p->name);
1684 }
1685 }
1686
StoreIC(const StoreICParameters * p)1687 void AccessorAssembler::StoreIC(const StoreICParameters* p) {
1688 Variable var_handler(this, MachineRepresentation::kTagged);
1689 // TODO(ishell): defer blocks when it works.
1690 Label if_handler(this, &var_handler), try_polymorphic(this),
1691 try_megamorphic(this /*, Label::kDeferred*/),
1692 miss(this /*, Label::kDeferred*/);
1693
1694 Node* receiver_map = LoadReceiverMap(p->receiver);
1695 GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(receiver_map)), &miss);
1696
1697 // Check monomorphic case.
1698 Node* feedback =
1699 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
1700 &var_handler, &try_polymorphic);
1701 Bind(&if_handler);
1702 {
1703 Comment("StoreIC_if_handler");
1704 HandleStoreICHandlerCase(p, var_handler.value(), &miss);
1705 }
1706
1707 Bind(&try_polymorphic);
1708 {
1709 // Check polymorphic case.
1710 Comment("StoreIC_try_polymorphic");
1711 GotoIfNot(
1712 WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
1713 &try_megamorphic);
1714 HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
1715 &miss, 2);
1716 }
1717
1718 Bind(&try_megamorphic);
1719 {
1720 // Check megamorphic case.
1721 GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
1722 &miss);
1723
1724 TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name,
1725 &if_handler, &var_handler, &miss);
1726 }
1727 Bind(&miss);
1728 {
1729 TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot,
1730 p->vector, p->receiver, p->name);
1731 }
1732 }
1733
KeyedStoreIC(const StoreICParameters * p,LanguageMode language_mode)1734 void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p,
1735 LanguageMode language_mode) {
1736 // TODO(ishell): defer blocks when it works.
1737 Label miss(this /*, Label::kDeferred*/);
1738 {
1739 Variable var_handler(this, MachineRepresentation::kTagged);
1740
1741 // TODO(ishell): defer blocks when it works.
1742 Label if_handler(this, &var_handler), try_polymorphic(this),
1743 try_megamorphic(this /*, Label::kDeferred*/),
1744 try_polymorphic_name(this /*, Label::kDeferred*/);
1745
1746 Node* receiver_map = LoadReceiverMap(p->receiver);
1747 GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(receiver_map)), &miss);
1748
1749 // Check monomorphic case.
1750 Node* feedback =
1751 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
1752 &var_handler, &try_polymorphic);
1753 Bind(&if_handler);
1754 {
1755 Comment("KeyedStoreIC_if_handler");
1756 HandleStoreICHandlerCase(p, var_handler.value(), &miss, kSupportElements);
1757 }
1758
1759 Bind(&try_polymorphic);
1760 {
1761 // CheckPolymorphic case.
1762 Comment("KeyedStoreIC_try_polymorphic");
1763 GotoIfNot(
1764 WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
1765 &try_megamorphic);
1766 Label if_transition_handler(this);
1767 Variable var_transition_map_cell(this, MachineRepresentation::kTagged);
1768 HandleKeyedStorePolymorphicCase(receiver_map, feedback, &if_handler,
1769 &var_handler, &if_transition_handler,
1770 &var_transition_map_cell, &miss);
1771 Bind(&if_transition_handler);
1772 Comment("KeyedStoreIC_polymorphic_transition");
1773 {
1774 Node* handler = var_handler.value();
1775
1776 Label call_handler(this);
1777 Variable var_code_handler(this, MachineRepresentation::kTagged);
1778 var_code_handler.Bind(handler);
1779 GotoIfNot(IsTuple2Map(LoadMap(handler)), &call_handler);
1780 {
1781 CSA_ASSERT(this, IsTuple2Map(LoadMap(handler)));
1782
1783 // Check validity cell.
1784 Node* validity_cell = LoadObjectField(handler, Tuple2::kValue1Offset);
1785 Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
1786 GotoIf(
1787 WordNotEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)),
1788 &miss);
1789
1790 var_code_handler.Bind(
1791 LoadObjectField(handler, Tuple2::kValue2Offset));
1792 Goto(&call_handler);
1793 }
1794
1795 Bind(&call_handler);
1796 {
1797 Node* code_handler = var_code_handler.value();
1798 CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
1799
1800 Node* transition_map =
1801 LoadWeakCellValue(var_transition_map_cell.value(), &miss);
1802 StoreTransitionDescriptor descriptor(isolate());
1803 TailCallStub(descriptor, code_handler, p->context, p->receiver,
1804 p->name, transition_map, p->value, p->slot, p->vector);
1805 }
1806 }
1807 }
1808
1809 Bind(&try_megamorphic);
1810 {
1811 // Check megamorphic case.
1812 Comment("KeyedStoreIC_try_megamorphic");
1813 GotoIfNot(
1814 WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
1815 &try_polymorphic_name);
1816 TailCallStub(
1817 CodeFactory::KeyedStoreIC_Megamorphic(isolate(), language_mode),
1818 p->context, p->receiver, p->name, p->value, p->slot, p->vector);
1819 }
1820
1821 Bind(&try_polymorphic_name);
1822 {
1823 // We might have a name in feedback, and a fixed array in the next slot.
1824 Comment("KeyedStoreIC_try_polymorphic_name");
1825 GotoIfNot(WordEqual(feedback, p->name), &miss);
1826 // If the name comparison succeeded, we know we have a FixedArray with
1827 // at least one map/handler pair.
1828 Node* offset = ElementOffsetFromIndex(
1829 p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
1830 FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
1831 Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
1832 HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler,
1833 &miss, 1);
1834 }
1835 }
1836 Bind(&miss);
1837 {
1838 Comment("KeyedStoreIC_miss");
1839 TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
1840 p->vector, p->receiver, p->name);
1841 }
1842 }
1843
1844 //////////////////// Public methods.
1845
GenerateLoadIC()1846 void AccessorAssembler::GenerateLoadIC() {
1847 typedef LoadWithVectorDescriptor Descriptor;
1848
1849 Node* receiver = Parameter(Descriptor::kReceiver);
1850 Node* name = Parameter(Descriptor::kName);
1851 Node* slot = Parameter(Descriptor::kSlot);
1852 Node* vector = Parameter(Descriptor::kVector);
1853 Node* context = Parameter(Descriptor::kContext);
1854
1855 LoadICParameters p(context, receiver, name, slot, vector);
1856 LoadIC(&p);
1857 }
1858
GenerateLoadICTrampoline()1859 void AccessorAssembler::GenerateLoadICTrampoline() {
1860 typedef LoadDescriptor Descriptor;
1861
1862 Node* receiver = Parameter(Descriptor::kReceiver);
1863 Node* name = Parameter(Descriptor::kName);
1864 Node* slot = Parameter(Descriptor::kSlot);
1865 Node* context = Parameter(Descriptor::kContext);
1866 Node* vector = LoadFeedbackVectorForStub();
1867
1868 LoadICParameters p(context, receiver, name, slot, vector);
1869 LoadIC(&p);
1870 }
1871
GenerateLoadICProtoArray(bool throw_reference_error_if_nonexistent)1872 void AccessorAssembler::GenerateLoadICProtoArray(
1873 bool throw_reference_error_if_nonexistent) {
1874 typedef LoadICProtoArrayDescriptor Descriptor;
1875
1876 Node* receiver = Parameter(Descriptor::kReceiver);
1877 Node* name = Parameter(Descriptor::kName);
1878 Node* slot = Parameter(Descriptor::kSlot);
1879 Node* vector = Parameter(Descriptor::kVector);
1880 Node* handler = Parameter(Descriptor::kHandler);
1881 Node* context = Parameter(Descriptor::kContext);
1882
1883 LoadICParameters p(context, receiver, name, slot, vector);
1884 LoadICProtoArray(&p, handler, throw_reference_error_if_nonexistent);
1885 }
1886
GenerateLoadField()1887 void AccessorAssembler::GenerateLoadField() {
1888 typedef LoadFieldDescriptor Descriptor;
1889
1890 Node* receiver = Parameter(Descriptor::kReceiver);
1891 Node* name = nullptr;
1892 Node* slot = nullptr;
1893 Node* vector = nullptr;
1894 Node* context = Parameter(Descriptor::kContext);
1895 LoadICParameters p(context, receiver, name, slot, vector);
1896
1897 ExitPoint direct_exit(this);
1898
1899 HandleLoadICSmiHandlerCase(&p, receiver, Parameter(Descriptor::kSmiHandler),
1900 nullptr, &direct_exit, kOnlyProperties);
1901 }
1902
GenerateLoadGlobalIC(TypeofMode typeof_mode)1903 void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) {
1904 typedef LoadGlobalWithVectorDescriptor Descriptor;
1905
1906 Node* name = Parameter(Descriptor::kName);
1907 Node* slot = Parameter(Descriptor::kSlot);
1908 Node* vector = Parameter(Descriptor::kVector);
1909 Node* context = Parameter(Descriptor::kContext);
1910
1911 LoadICParameters p(context, nullptr, name, slot, vector);
1912 LoadGlobalIC(&p, typeof_mode);
1913 }
1914
GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode)1915 void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) {
1916 typedef LoadGlobalDescriptor Descriptor;
1917
1918 Node* name = Parameter(Descriptor::kName);
1919 Node* slot = Parameter(Descriptor::kSlot);
1920 Node* context = Parameter(Descriptor::kContext);
1921 Node* vector = LoadFeedbackVectorForStub();
1922
1923 LoadICParameters p(context, nullptr, name, slot, vector);
1924 LoadGlobalIC(&p, typeof_mode);
1925 }
1926
GenerateKeyedLoadIC()1927 void AccessorAssembler::GenerateKeyedLoadIC() {
1928 typedef LoadWithVectorDescriptor Descriptor;
1929
1930 Node* receiver = Parameter(Descriptor::kReceiver);
1931 Node* name = Parameter(Descriptor::kName);
1932 Node* slot = Parameter(Descriptor::kSlot);
1933 Node* vector = Parameter(Descriptor::kVector);
1934 Node* context = Parameter(Descriptor::kContext);
1935
1936 LoadICParameters p(context, receiver, name, slot, vector);
1937 KeyedLoadIC(&p);
1938 }
1939
GenerateKeyedLoadICTrampoline()1940 void AccessorAssembler::GenerateKeyedLoadICTrampoline() {
1941 typedef LoadDescriptor Descriptor;
1942
1943 Node* receiver = Parameter(Descriptor::kReceiver);
1944 Node* name = Parameter(Descriptor::kName);
1945 Node* slot = Parameter(Descriptor::kSlot);
1946 Node* context = Parameter(Descriptor::kContext);
1947 Node* vector = LoadFeedbackVectorForStub();
1948
1949 LoadICParameters p(context, receiver, name, slot, vector);
1950 KeyedLoadIC(&p);
1951 }
1952
GenerateKeyedLoadIC_Megamorphic()1953 void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() {
1954 typedef LoadWithVectorDescriptor Descriptor;
1955
1956 Node* receiver = Parameter(Descriptor::kReceiver);
1957 Node* name = Parameter(Descriptor::kName);
1958 Node* slot = Parameter(Descriptor::kSlot);
1959 Node* vector = Parameter(Descriptor::kVector);
1960 Node* context = Parameter(Descriptor::kContext);
1961
1962 LoadICParameters p(context, receiver, name, slot, vector);
1963 KeyedLoadICGeneric(&p);
1964 }
1965
GenerateStoreIC()1966 void AccessorAssembler::GenerateStoreIC() {
1967 typedef StoreWithVectorDescriptor Descriptor;
1968
1969 Node* receiver = Parameter(Descriptor::kReceiver);
1970 Node* name = Parameter(Descriptor::kName);
1971 Node* value = Parameter(Descriptor::kValue);
1972 Node* slot = Parameter(Descriptor::kSlot);
1973 Node* vector = Parameter(Descriptor::kVector);
1974 Node* context = Parameter(Descriptor::kContext);
1975
1976 StoreICParameters p(context, receiver, name, value, slot, vector);
1977 StoreIC(&p);
1978 }
1979
GenerateStoreICTrampoline()1980 void AccessorAssembler::GenerateStoreICTrampoline() {
1981 typedef StoreDescriptor Descriptor;
1982
1983 Node* receiver = Parameter(Descriptor::kReceiver);
1984 Node* name = Parameter(Descriptor::kName);
1985 Node* value = Parameter(Descriptor::kValue);
1986 Node* slot = Parameter(Descriptor::kSlot);
1987 Node* context = Parameter(Descriptor::kContext);
1988 Node* vector = LoadFeedbackVectorForStub();
1989
1990 StoreICParameters p(context, receiver, name, value, slot, vector);
1991 StoreIC(&p);
1992 }
1993
GenerateKeyedStoreIC(LanguageMode language_mode)1994 void AccessorAssembler::GenerateKeyedStoreIC(LanguageMode language_mode) {
1995 typedef StoreWithVectorDescriptor Descriptor;
1996
1997 Node* receiver = Parameter(Descriptor::kReceiver);
1998 Node* name = Parameter(Descriptor::kName);
1999 Node* value = Parameter(Descriptor::kValue);
2000 Node* slot = Parameter(Descriptor::kSlot);
2001 Node* vector = Parameter(Descriptor::kVector);
2002 Node* context = Parameter(Descriptor::kContext);
2003
2004 StoreICParameters p(context, receiver, name, value, slot, vector);
2005 KeyedStoreIC(&p, language_mode);
2006 }
2007
GenerateKeyedStoreICTrampoline(LanguageMode language_mode)2008 void AccessorAssembler::GenerateKeyedStoreICTrampoline(
2009 LanguageMode language_mode) {
2010 typedef StoreDescriptor Descriptor;
2011
2012 Node* receiver = Parameter(Descriptor::kReceiver);
2013 Node* name = Parameter(Descriptor::kName);
2014 Node* value = Parameter(Descriptor::kValue);
2015 Node* slot = Parameter(Descriptor::kSlot);
2016 Node* context = Parameter(Descriptor::kContext);
2017 Node* vector = LoadFeedbackVectorForStub();
2018
2019 StoreICParameters p(context, receiver, name, value, slot, vector);
2020 KeyedStoreIC(&p, language_mode);
2021 }
2022
2023 } // namespace internal
2024 } // namespace v8
2025