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/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/ic.h"
12 : #include "src/ic/stub-cache.h"
13 : #include "src/objects-inl.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : using compiler::CodeAssemblerState;
19 : using compiler::Node;
20 :
21 : //////////////////// Private helpers.
22 :
23 217 : Node* AccessorAssembler::TryMonomorphicCase(Node* slot, Node* vector,
24 : Node* receiver_map,
25 : Label* if_handler,
26 : Variable* var_handler,
27 : Label* if_miss) {
28 217 : Comment("TryMonomorphicCase");
29 : DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
30 :
31 : // TODO(ishell): add helper class that hides offset computations for a series
32 : // of loads.
33 : CSA_ASSERT(this, IsFeedbackVector(vector), vector);
34 : int32_t header_size = FeedbackVector::kFeedbackSlotsOffset - kHeapObjectTag;
35 : // Adding |header_size| with a separate IntPtrAdd rather than passing it
36 : // into ElementOffsetFromIndex() allows it to be folded into a single
37 : // [base, index, offset] indirect memory access on x64.
38 217 : Node* offset = ElementOffsetFromIndex(slot, HOLEY_ELEMENTS, SMI_PARAMETERS);
39 : Node* feedback = Load(MachineType::AnyTagged(), vector,
40 651 : IntPtrAdd(offset, IntPtrConstant(header_size)));
41 :
42 : // Try to quickly handle the monomorphic case without knowing for sure
43 : // if we have a weak cell in feedback. We do know it's safe to look
44 : // at WeakCell::kValueOffset.
45 434 : GotoIf(WordNotEqual(receiver_map, LoadWeakCellValueUnchecked(feedback)),
46 434 : if_miss);
47 :
48 : Node* handler =
49 : Load(MachineType::AnyTagged(), vector,
50 651 : IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize)));
51 :
52 217 : var_handler->Bind(handler);
53 217 : Goto(if_handler);
54 217 : return feedback;
55 : }
56 :
57 279 : void AccessorAssembler::HandlePolymorphicCase(Node* receiver_map,
58 : Node* feedback, Label* if_handler,
59 : Variable* var_handler,
60 : Label* if_miss,
61 : int min_feedback_capacity) {
62 279 : Comment("HandlePolymorphicCase");
63 : DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
64 :
65 : // Deferred so the unrolled case can omit frame construction in bytecode
66 : // handler.
67 : Label loop(this, Label::kDeferred);
68 :
69 : // Iterate {feedback} array.
70 : const int kEntrySize = 2;
71 :
72 : // Loading feedback's length is delayed until we need it when looking past
73 : // the first {min_feedback_capacity} (map, handler) pairs.
74 : Node* length = nullptr;
75 : CSA_ASSERT(this, SmiGreaterThanOrEqual(
76 : LoadFixedArrayBaseLength(feedback),
77 : SmiConstant(min_feedback_capacity * kEntrySize)));
78 :
79 : const int kUnrolledIterations = IC::kMaxPolymorphicMapCount;
80 1395 : for (int i = 0; i < kUnrolledIterations; i++) {
81 1116 : int map_index = i * kEntrySize;
82 1116 : int handler_index = i * kEntrySize + 1;
83 :
84 1116 : if (i >= min_feedback_capacity) {
85 1178 : if (length == nullptr) length = LoadFixedArrayBaseLength(feedback);
86 : GotoIf(SmiGreaterThanOrEqual(SmiConstant(handler_index), length),
87 1860 : if_miss);
88 : }
89 :
90 : Label next_entry(this);
91 : Node* cached_map =
92 1116 : LoadWeakCellValue(LoadFixedArrayElement(feedback, map_index));
93 2232 : GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
94 :
95 : // Found, now call handler.
96 1116 : Node* handler = LoadFixedArrayElement(feedback, handler_index);
97 1116 : var_handler->Bind(handler);
98 1116 : Goto(if_handler);
99 :
100 1116 : BIND(&next_entry);
101 1116 : }
102 279 : Goto(&loop);
103 :
104 : // Loop from {kUnrolledIterations}*kEntrySize to {length}.
105 279 : BIND(&loop);
106 558 : Node* start_index = IntPtrConstant(kUnrolledIterations * kEntrySize);
107 558 : Node* end_index = LoadAndUntagFixedArrayBaseLength(feedback);
108 : BuildFastLoop(
109 : start_index, end_index,
110 279 : [this, receiver_map, feedback, if_handler, var_handler](Node* index) {
111 : Node* cached_map =
112 279 : LoadWeakCellValue(LoadFixedArrayElement(feedback, index));
113 :
114 279 : Label next_entry(this);
115 837 : GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
116 :
117 : // Found, now call handler.
118 279 : Node* handler = LoadFixedArrayElement(feedback, index, kPointerSize);
119 279 : var_handler->Bind(handler);
120 279 : Goto(if_handler);
121 :
122 279 : BIND(&next_entry);
123 279 : },
124 558 : kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
125 : // The loop falls through if no handler was found.
126 279 : Goto(if_miss);
127 279 : }
128 :
129 248 : void AccessorAssembler::HandleLoadICHandlerCase(
130 : const LoadICParameters* p, Node* handler, Label* miss,
131 : ExitPoint* exit_point, ElementSupport support_elements) {
132 248 : Comment("have_handler");
133 :
134 248 : VARIABLE(var_holder, MachineRepresentation::kTagged, p->receiver);
135 496 : VARIABLE(var_smi_handler, MachineRepresentation::kTagged, handler);
136 :
137 248 : Variable* vars[] = {&var_holder, &var_smi_handler};
138 496 : Label if_smi_handler(this, 2, vars);
139 248 : Label try_proto_handler(this, Label::kDeferred),
140 248 : call_handler(this, Label::kDeferred);
141 :
142 496 : Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
143 :
144 : // |handler| is a Smi, encoding what to do. See SmiHandler methods
145 : // for the encoding format.
146 248 : BIND(&if_smi_handler);
147 : {
148 : HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(),
149 248 : miss, exit_point, false, support_elements);
150 : }
151 :
152 248 : BIND(&try_proto_handler);
153 : {
154 744 : GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
155 : HandleLoadICProtoHandlerCase(p, handler, &var_holder, &var_smi_handler,
156 248 : &if_smi_handler, miss, exit_point, false);
157 : }
158 :
159 248 : BIND(&call_handler);
160 : {
161 : typedef LoadWithVectorDescriptor Descriptor;
162 : exit_point->ReturnCallStub(Descriptor(isolate()), handler, p->context,
163 496 : p->receiver, p->name, p->slot, p->vector);
164 248 : }
165 248 : }
166 :
167 1209 : void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word,
168 : Variable* var_double_value,
169 : Label* rebox_double,
170 : ExitPoint* exit_point) {
171 1209 : Comment("field_load");
172 1209 : Node* offset = DecodeWord<LoadHandler::FieldOffsetBits>(handler_word);
173 :
174 1209 : Label inobject(this), out_of_object(this);
175 : Branch(IsSetWord<LoadHandler::IsInobjectBits>(handler_word), &inobject,
176 1209 : &out_of_object);
177 :
178 1209 : BIND(&inobject);
179 : {
180 : Label is_double(this);
181 1209 : GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
182 1209 : exit_point->Return(LoadObjectField(holder, offset));
183 :
184 1209 : BIND(&is_double);
185 : if (FLAG_unbox_double_fields) {
186 : var_double_value->Bind(
187 1209 : LoadObjectField(holder, offset, MachineType::Float64()));
188 : } else {
189 : Node* mutable_heap_number = LoadObjectField(holder, offset);
190 : var_double_value->Bind(LoadHeapNumberValue(mutable_heap_number));
191 : }
192 1209 : Goto(rebox_double);
193 : }
194 :
195 1209 : BIND(&out_of_object);
196 : {
197 : Label is_double(this);
198 2418 : Node* properties = LoadFastProperties(holder);
199 : Node* value = LoadObjectField(properties, offset);
200 1209 : GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
201 1209 : exit_point->Return(value);
202 :
203 1209 : BIND(&is_double);
204 2418 : var_double_value->Bind(LoadHeapNumberValue(value));
205 1209 : Goto(rebox_double);
206 1209 : }
207 1209 : }
208 :
209 1178 : void AccessorAssembler::HandleLoadICSmiHandlerCase(
210 : const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss,
211 : ExitPoint* exit_point, bool throw_reference_error_if_nonexistent,
212 : ElementSupport support_elements) {
213 1178 : VARIABLE(var_double_value, MachineRepresentation::kFloat64);
214 1178 : Label rebox_double(this, &var_double_value);
215 :
216 2356 : Node* handler_word = SmiUntag(smi_handler);
217 : Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
218 1178 : if (support_elements == kSupportElements) {
219 : Label property(this);
220 62 : GotoIfNot(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
221 62 : &property);
222 :
223 31 : Comment("element_load");
224 31 : Node* intptr_index = TryToIntptr(p->name, miss);
225 62 : Node* elements = LoadElements(holder);
226 : Node* is_jsarray_condition =
227 : IsSetWord<LoadHandler::IsJsArrayBits>(handler_word);
228 : Node* elements_kind =
229 31 : DecodeWord32FromWord<LoadHandler::ElementsKindBits>(handler_word);
230 31 : Label if_hole(this), unimplemented_elements_kind(this);
231 : Label* out_of_bounds = miss;
232 : EmitElementLoad(holder, elements, elements_kind, intptr_index,
233 : is_jsarray_condition, &if_hole, &rebox_double,
234 : &var_double_value, &unimplemented_elements_kind,
235 31 : out_of_bounds, miss, exit_point);
236 :
237 31 : BIND(&unimplemented_elements_kind);
238 : {
239 : // Smi handlers should only be installed for supported elements kinds.
240 : // Crash if we get here.
241 31 : DebugBreak();
242 31 : Goto(miss);
243 : }
244 :
245 31 : BIND(&if_hole);
246 : {
247 31 : Comment("convert hole");
248 31 : GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
249 62 : GotoIf(IsArrayProtectorCellInvalid(), miss);
250 62 : exit_point->Return(UndefinedConstant());
251 : }
252 :
253 31 : BIND(&property);
254 62 : Comment("property_load");
255 : }
256 :
257 1178 : Label constant(this), field(this), normal(this, Label::kDeferred),
258 1178 : interceptor(this, Label::kDeferred), nonexistent(this),
259 1178 : accessor(this, Label::kDeferred), global(this, Label::kDeferred),
260 1178 : module_export(this, Label::kDeferred), proxy(this, Label::kDeferred);
261 3534 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
262 :
263 2356 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
264 2356 : &constant);
265 :
266 2356 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
267 2356 : &nonexistent);
268 :
269 2356 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)),
270 2356 : &normal);
271 :
272 2356 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
273 2356 : &accessor);
274 :
275 2356 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)),
276 2356 : &global);
277 :
278 3534 : GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy);
279 :
280 2356 : Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kModuleExport)),
281 2356 : &module_export, &interceptor);
282 :
283 1178 : BIND(&field);
284 : HandleLoadField(holder, handler_word, &var_double_value, &rebox_double,
285 1178 : exit_point);
286 :
287 1178 : BIND(&nonexistent);
288 : // This is a handler for a load of a non-existent value.
289 1178 : if (throw_reference_error_if_nonexistent) {
290 : exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
291 465 : p->name);
292 : } else {
293 1426 : exit_point->Return(UndefinedConstant());
294 : }
295 :
296 1178 : BIND(&constant);
297 : {
298 1178 : Comment("constant_load");
299 3534 : Node* descriptors = LoadMapDescriptors(LoadMap(holder));
300 : Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
301 : Node* scaled_descriptor =
302 3534 : IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
303 : Node* value_index =
304 : IntPtrAdd(scaled_descriptor,
305 : IntPtrConstant(DescriptorArray::kFirstIndex +
306 3534 : DescriptorArray::kEntryValueIndex));
307 : CSA_ASSERT(this,
308 : UintPtrLessThan(descriptor,
309 : LoadAndUntagFixedArrayBaseLength(descriptors)));
310 1178 : Node* value = LoadFixedArrayElement(descriptors, value_index);
311 :
312 : Label if_accessor_info(this, Label::kDeferred);
313 : GotoIf(IsSetWord<LoadHandler::IsAccessorInfoBits>(handler_word),
314 1178 : &if_accessor_info);
315 1178 : exit_point->Return(value);
316 :
317 1178 : BIND(&if_accessor_info);
318 1178 : Callable callable = CodeFactory::ApiGetter(isolate());
319 : exit_point->ReturnCallStub(callable, p->context, p->receiver, holder,
320 2356 : value);
321 : }
322 :
323 1178 : BIND(&normal);
324 : {
325 1178 : Comment("load_normal");
326 2356 : Node* properties = LoadSlowProperties(holder);
327 1178 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
328 1178 : Label found(this, &var_name_index);
329 : NameDictionaryLookup<NameDictionary>(properties, p->name, &found,
330 1178 : &var_name_index, miss);
331 1178 : BIND(&found);
332 : {
333 1178 : VARIABLE(var_details, MachineRepresentation::kWord32);
334 2356 : VARIABLE(var_value, MachineRepresentation::kTagged);
335 : LoadPropertyFromNameDictionary(properties, var_name_index.value(),
336 1178 : &var_details, &var_value);
337 : Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
338 1178 : p->context, p->receiver, miss);
339 2356 : exit_point->Return(value);
340 1178 : }
341 : }
342 :
343 1178 : BIND(&accessor);
344 : {
345 1178 : Comment("accessor_load");
346 3534 : Node* descriptors = LoadMapDescriptors(LoadMap(holder));
347 : Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
348 : Node* scaled_descriptor =
349 3534 : IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
350 : Node* value_index =
351 : IntPtrAdd(scaled_descriptor,
352 : IntPtrConstant(DescriptorArray::kFirstIndex +
353 3534 : DescriptorArray::kEntryValueIndex));
354 : CSA_ASSERT(this,
355 : UintPtrLessThan(descriptor,
356 : LoadAndUntagFixedArrayBaseLength(descriptors)));
357 1178 : Node* accessor_pair = LoadFixedArrayElement(descriptors, value_index);
358 : CSA_ASSERT(this, IsAccessorPair(accessor_pair));
359 : Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
360 : CSA_ASSERT(this, Word32BinaryNot(IsTheHole(getter)));
361 :
362 1178 : Callable callable = CodeFactory::Call(isolate());
363 1178 : exit_point->Return(CallJS(callable, p->context, getter, p->receiver));
364 : }
365 :
366 1178 : BIND(&proxy);
367 : {
368 1178 : VARIABLE(var_index, MachineType::PointerRepresentation());
369 2356 : VARIABLE(var_unique, MachineRepresentation::kTagged);
370 :
371 1178 : Label if_index(this), if_unique_name(this),
372 1178 : to_name_failed(this, Label::kDeferred);
373 :
374 1178 : if (support_elements == kSupportElements) {
375 : TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
376 31 : &to_name_failed);
377 :
378 31 : BIND(&if_unique_name);
379 : exit_point->ReturnCallStub(
380 : Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
381 62 : p->context, holder, var_unique.value(), p->receiver);
382 :
383 31 : BIND(&if_index);
384 : // TODO(mslekova): introduce TryToName that doesn't try to compute
385 : // the intptr index value
386 31 : Goto(&to_name_failed);
387 :
388 31 : BIND(&to_name_failed);
389 : exit_point->ReturnCallRuntime(Runtime::kGetPropertyWithReceiver,
390 31 : p->context, holder, p->name, p->receiver);
391 :
392 : } else {
393 : exit_point->ReturnCallStub(
394 : Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
395 2294 : p->context, holder, p->name, p->receiver);
396 1178 : }
397 : }
398 :
399 1178 : BIND(&global);
400 : {
401 : CSA_ASSERT(this, IsPropertyCell(holder));
402 : // Ensure the property cell doesn't contain the hole.
403 : Node* value = LoadObjectField(holder, PropertyCell::kValueOffset);
404 : Node* details =
405 2356 : LoadAndUntagToWord32ObjectField(holder, PropertyCell::kDetailsOffset);
406 2356 : GotoIf(IsTheHole(value), miss);
407 :
408 : exit_point->Return(
409 1178 : CallGetterIfAccessor(value, details, p->context, p->receiver, miss));
410 : }
411 :
412 1178 : BIND(&interceptor);
413 : {
414 1178 : Comment("load_interceptor");
415 : exit_point->ReturnCallRuntime(Runtime::kLoadPropertyWithInterceptor,
416 : p->context, p->name, p->receiver, holder,
417 1178 : p->slot, p->vector);
418 : }
419 :
420 1178 : BIND(&module_export);
421 : {
422 1178 : Comment("module export");
423 : Node* index = DecodeWord<LoadHandler::ExportsIndexBits>(handler_word);
424 : Node* module =
425 : LoadObjectField(p->receiver, JSModuleNamespace::kModuleOffset,
426 2356 : MachineType::TaggedPointer());
427 : Node* exports = LoadObjectField(module, Module::kExportsOffset,
428 1178 : MachineType::TaggedPointer());
429 1178 : Node* cell = LoadFixedArrayElement(exports, index);
430 : // The handler is only installed for exports that exist.
431 : CSA_ASSERT(this, IsCell(cell));
432 1178 : Node* value = LoadCellValue(cell);
433 : Label is_the_hole(this, Label::kDeferred);
434 2356 : GotoIf(IsTheHole(value), &is_the_hole);
435 1178 : exit_point->Return(value);
436 :
437 1178 : BIND(&is_the_hole);
438 : {
439 2356 : Node* message = SmiConstant(MessageTemplate::kNotDefined);
440 : exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
441 1178 : message, p->name);
442 1178 : }
443 : }
444 :
445 1178 : BIND(&rebox_double);
446 4712 : exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value()));
447 1178 : }
448 :
449 682 : void AccessorAssembler::HandleLoadICProtoHandlerCase(
450 : const LoadICParameters* p, Node* handler, Variable* var_holder,
451 : Variable* var_smi_handler, Label* if_smi_handler, Label* miss,
452 : ExitPoint* exit_point, bool throw_reference_error_if_nonexistent) {
453 : DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
454 : DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
455 :
456 : // IC dispatchers rely on these assumptions to be held.
457 : STATIC_ASSERT(FixedArray::kLengthOffset == LoadHandler::kHolderCellOffset);
458 : DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kSmiHandlerIndex),
459 : LoadHandler::kSmiHandlerOffset);
460 : DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kValidityCellIndex),
461 : LoadHandler::kValidityCellOffset);
462 :
463 : // Both FixedArray and Tuple3 handlers have validity cell at the same offset.
464 682 : Label validity_cell_check_done(this);
465 : Node* validity_cell =
466 682 : LoadObjectField(handler, LoadHandler::kValidityCellOffset);
467 1364 : GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
468 1364 : &validity_cell_check_done);
469 : Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
470 1364 : GotoIf(WordNotEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)),
471 682 : miss);
472 682 : Goto(&validity_cell_check_done);
473 :
474 682 : BIND(&validity_cell_check_done);
475 : Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
476 : CSA_ASSERT(this, TaggedIsSmi(smi_handler));
477 1364 : Node* handler_flags = SmiUntag(smi_handler);
478 :
479 682 : Label check_prototypes(this);
480 : GotoIfNot(IsSetWord<LoadHandler::LookupOnReceiverBits>(handler_flags),
481 682 : &check_prototypes);
482 : {
483 : CSA_ASSERT(this, Word32BinaryNot(
484 : HasInstanceType(p->receiver, JS_GLOBAL_OBJECT_TYPE)));
485 2046 : Node* properties = LoadSlowProperties(p->receiver);
486 682 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
487 682 : Label found(this, &var_name_index);
488 : NameDictionaryLookup<NameDictionary>(properties, p->name, &found,
489 682 : &var_name_index, &check_prototypes);
490 682 : BIND(&found);
491 : {
492 682 : VARIABLE(var_details, MachineRepresentation::kWord32);
493 1364 : VARIABLE(var_value, MachineRepresentation::kTagged);
494 : LoadPropertyFromNameDictionary(properties, var_name_index.value(),
495 682 : &var_details, &var_value);
496 : Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
497 682 : p->context, p->receiver, miss);
498 1364 : exit_point->Return(value);
499 682 : }
500 : }
501 :
502 682 : BIND(&check_prototypes);
503 : Node* maybe_holder_cell =
504 : LoadObjectField(handler, LoadHandler::kHolderCellOffset);
505 682 : Label array_handler(this), tuple_handler(this);
506 1364 : Branch(TaggedIsSmi(maybe_holder_cell), &array_handler, &tuple_handler);
507 :
508 682 : BIND(&tuple_handler);
509 : {
510 682 : Label load_from_cached_holder(this), done(this);
511 :
512 1364 : Branch(WordEqual(maybe_holder_cell, NullConstant()), &done,
513 682 : &load_from_cached_holder);
514 :
515 682 : BIND(&load_from_cached_holder);
516 : {
517 : // For regular holders, having passed the receiver map check and the
518 : // validity cell check implies that |holder| is alive. However, for
519 : // global object receivers, the |maybe_holder_cell| may be cleared.
520 682 : Node* holder = LoadWeakCellValue(maybe_holder_cell, miss);
521 :
522 682 : var_holder->Bind(holder);
523 682 : Goto(&done);
524 : }
525 :
526 682 : BIND(&done);
527 682 : var_smi_handler->Bind(smi_handler);
528 1364 : Goto(if_smi_handler);
529 : }
530 :
531 682 : BIND(&array_handler);
532 : {
533 : exit_point->ReturnCallStub(
534 : CodeFactory::LoadICProtoArray(isolate(),
535 : throw_reference_error_if_nonexistent),
536 1364 : p->context, p->receiver, p->name, p->slot, p->vector, handler);
537 682 : }
538 682 : }
539 :
540 217 : void AccessorAssembler::EmitAccessCheck(Node* expected_native_context,
541 : Node* context, Node* receiver,
542 : Label* can_access, Label* miss) {
543 : CSA_ASSERT(this, IsNativeContext(expected_native_context));
544 :
545 434 : Node* native_context = LoadNativeContext(context);
546 434 : GotoIf(WordEqual(expected_native_context, native_context), can_access);
547 : // If the receiver is not a JSGlobalProxy then we miss.
548 434 : GotoIfNot(IsJSGlobalProxy(receiver), miss);
549 : // For JSGlobalProxy receiver try to compare security tokens of current
550 : // and expected native contexts.
551 : Node* expected_token = LoadContextElement(expected_native_context,
552 434 : Context::SECURITY_TOKEN_INDEX);
553 : Node* current_token =
554 434 : LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
555 434 : Branch(WordEqual(expected_token, current_token), can_access, miss);
556 217 : }
557 :
558 62 : Node* AccessorAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p,
559 : Node* handler,
560 : Node* handler_length,
561 : Node* handler_flags,
562 : Label* miss) {
563 124 : VARIABLE(var_start_index, MachineType::PointerRepresentation(),
564 : IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
565 :
566 62 : Label can_access(this);
567 : GotoIfNot(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
568 124 : &can_access);
569 : {
570 : // Skip this entry of a handler.
571 124 : var_start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
572 :
573 : int offset =
574 : FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex);
575 : Node* expected_native_context =
576 62 : LoadWeakCellValue(LoadObjectField(handler, offset), miss);
577 :
578 : EmitAccessCheck(expected_native_context, p->context, p->receiver,
579 62 : &can_access, miss);
580 : }
581 62 : BIND(&can_access);
582 :
583 : BuildFastLoop(var_start_index.value(), handler_length,
584 62 : [=](Node* current) {
585 : Node* prototype_cell =
586 62 : LoadFixedArrayElement(handler, current);
587 62 : CheckPrototype(prototype_cell, p->name, miss);
588 62 : },
589 124 : 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
590 :
591 : Node* maybe_holder_cell =
592 62 : LoadFixedArrayElement(handler, LoadHandler::kHolderCellIndex);
593 :
594 124 : VARIABLE(var_holder, MachineRepresentation::kTagged, p->receiver);
595 62 : Label done(this);
596 124 : GotoIf(WordEqual(maybe_holder_cell, NullConstant()), &done);
597 :
598 : {
599 : // For regular holders, having passed the receiver map check and the
600 : // validity cell check implies that |holder| is alive. However, for
601 : // global object receivers, the |maybe_holder_cell| may be cleared.
602 62 : var_holder.Bind(LoadWeakCellValue(maybe_holder_cell, miss));
603 62 : Goto(&done);
604 : }
605 :
606 62 : BIND(&done);
607 124 : return var_holder.value();
608 : }
609 :
610 434 : void AccessorAssembler::HandleLoadGlobalICHandlerCase(
611 : const LoadICParameters* pp, Node* handler, Label* miss,
612 : ExitPoint* exit_point, bool throw_reference_error_if_nonexistent) {
613 434 : LoadICParameters p = *pp;
614 : DCHECK_NULL(p.receiver);
615 1302 : Node* native_context = LoadNativeContext(p.context);
616 868 : p.receiver = LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX);
617 :
618 868 : VARIABLE(var_holder, MachineRepresentation::kTagged,
619 : LoadContextElement(native_context, Context::EXTENSION_INDEX));
620 868 : VARIABLE(var_smi_handler, MachineRepresentation::kTagged);
621 434 : Label if_smi_handler(this);
622 :
623 : HandleLoadICProtoHandlerCase(&p, handler, &var_holder, &var_smi_handler,
624 : &if_smi_handler, miss, exit_point,
625 434 : throw_reference_error_if_nonexistent);
626 434 : BIND(&if_smi_handler);
627 : HandleLoadICSmiHandlerCase(
628 : &p, var_holder.value(), var_smi_handler.value(), miss, exit_point,
629 868 : throw_reference_error_if_nonexistent, kOnlyProperties);
630 434 : }
631 :
632 310 : void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
633 : Label* readonly) {
634 : // Accessor properties never have the READ_ONLY attribute set.
635 620 : GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
636 620 : readonly);
637 : Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
638 930 : GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
639 : // Fall through if it's an accessor property.
640 310 : }
641 :
642 155 : void AccessorAssembler::HandleStoreICHandlerCase(
643 : const StoreICParameters* p, Node* handler, Label* miss,
644 : ElementSupport support_elements) {
645 310 : Label if_smi_handler(this), if_nonsmi_handler(this);
646 155 : Label if_proto_handler(this), if_element_handler(this), call_handler(this),
647 155 : store_global(this);
648 :
649 310 : Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler);
650 :
651 : // |handler| is a Smi, encoding what to do. See SmiHandler methods
652 : // for the encoding format.
653 155 : BIND(&if_smi_handler);
654 : {
655 155 : Node* holder = p->receiver;
656 310 : Node* handler_word = SmiUntag(handler);
657 :
658 155 : Label if_fast_smi(this), if_proxy(this);
659 :
660 : STATIC_ASSERT(StoreHandler::kStoreGlobalProxy + 1 ==
661 : StoreHandler::kStoreNormal);
662 : STATIC_ASSERT(StoreHandler::kStoreNormal + 1 == StoreHandler::kProxy);
663 : STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber);
664 :
665 : Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
666 : GotoIf(IntPtrLessThan(handler_kind,
667 310 : IntPtrConstant(StoreHandler::kStoreGlobalProxy)),
668 310 : &if_fast_smi);
669 310 : GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)),
670 310 : &if_proxy);
671 : CSA_ASSERT(this, WordEqual(handler_kind,
672 : IntPtrConstant(StoreHandler::kStoreNormal)));
673 310 : Node* properties = LoadSlowProperties(holder);
674 :
675 310 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
676 155 : Label dictionary_found(this, &var_name_index);
677 : NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
678 155 : &var_name_index, miss);
679 155 : BIND(&dictionary_found);
680 : {
681 : Node* details = LoadDetailsByKeyIndex<NameDictionary>(
682 155 : properties, var_name_index.value());
683 : // Check that the property is a writable data property (no accessor).
684 : const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask |
685 : PropertyDetails::kAttributesReadOnlyMask;
686 : STATIC_ASSERT(kData == 0);
687 310 : GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
688 :
689 : StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(),
690 155 : p->value);
691 310 : Return(p->value);
692 : }
693 :
694 155 : BIND(&if_fast_smi);
695 : // Handle non-transitioning field stores.
696 155 : HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
697 :
698 155 : BIND(&if_proxy);
699 310 : HandleStoreToProxy(p, holder, miss, support_elements);
700 : }
701 :
702 155 : BIND(&if_nonsmi_handler);
703 : {
704 310 : Node* handler_map = LoadMap(handler);
705 155 : if (support_elements == kSupportElements) {
706 62 : GotoIf(IsTuple2Map(handler_map), &if_element_handler);
707 : }
708 310 : GotoIf(IsWeakCellMap(handler_map), &store_global);
709 310 : Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler);
710 : }
711 :
712 155 : if (support_elements == kSupportElements) {
713 31 : BIND(&if_element_handler);
714 31 : HandleStoreICElementHandlerCase(p, handler, miss);
715 : }
716 :
717 155 : BIND(&if_proto_handler);
718 155 : HandleStoreICProtoHandler(p, handler, miss, support_elements);
719 :
720 : // |handler| is a heap object. Must be code, call it.
721 155 : BIND(&call_handler);
722 : {
723 155 : StoreWithVectorDescriptor descriptor(isolate());
724 : TailCallStub(descriptor, handler, p->context, p->receiver, p->name,
725 155 : p->value, p->slot, p->vector);
726 : }
727 :
728 155 : BIND(&store_global);
729 : {
730 : // Load value or miss if the {handler} weak cell is cleared.
731 155 : Node* cell = LoadWeakCellValue(handler, miss);
732 :
733 : ExitPoint direct_exit(this);
734 155 : StoreGlobalIC_PropertyCellCase(cell, p->value, &direct_exit, miss);
735 155 : }
736 155 : }
737 :
738 31 : void AccessorAssembler::HandleStoreICElementHandlerCase(
739 : const StoreICParameters* p, Node* handler, Label* miss) {
740 31 : Comment("HandleStoreICElementHandlerCase");
741 31 : Node* validity_cell = LoadObjectField(handler, Tuple2::kValue1Offset);
742 : Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
743 62 : GotoIf(WordNotEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)),
744 31 : miss);
745 :
746 : Node* code_handler = LoadObjectField(handler, Tuple2::kValue2Offset);
747 : CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
748 :
749 31 : StoreWithVectorDescriptor descriptor(isolate());
750 : TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name,
751 31 : p->value, p->slot, p->vector);
752 31 : }
753 :
754 155 : void AccessorAssembler::HandleStoreICProtoHandler(
755 : const StoreICParameters* p, Node* handler, Label* miss,
756 : ElementSupport support_elements) {
757 155 : Comment("HandleStoreICProtoHandler");
758 :
759 : // IC dispatchers rely on these assumptions to be held.
760 : STATIC_ASSERT(FixedArray::kLengthOffset ==
761 : StoreHandler::kTransitionOrHolderCellOffset);
762 : DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex),
763 : StoreHandler::kSmiHandlerOffset);
764 : DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex),
765 : StoreHandler::kValidityCellOffset);
766 :
767 : // Both FixedArray and Tuple3 handlers have validity cell at the same offset.
768 : Label validity_cell_check_done(this);
769 : Node* validity_cell =
770 155 : LoadObjectField(handler, StoreHandler::kValidityCellOffset);
771 310 : GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
772 310 : &validity_cell_check_done);
773 : Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
774 310 : GotoIf(WordNotEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)),
775 155 : miss);
776 155 : Goto(&validity_cell_check_done);
777 :
778 155 : BIND(&validity_cell_check_done);
779 : Node* smi_or_code = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
780 :
781 : Node* maybe_transition_cell =
782 : LoadObjectField(handler, StoreHandler::kTransitionOrHolderCellOffset);
783 155 : Label array_handler(this), tuple_handler(this);
784 310 : Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler);
785 :
786 310 : VARIABLE(var_transition_map_or_holder, MachineRepresentation::kTagged);
787 155 : Label do_store(this), if_transition_map(this), if_holder_object(this);
788 155 : BIND(&tuple_handler);
789 : {
790 155 : Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
791 155 : var_transition_map_or_holder.Bind(transition);
792 155 : Goto(&do_store);
793 : }
794 :
795 155 : BIND(&array_handler);
796 : {
797 310 : VARIABLE(var_start_index, MachineType::PointerRepresentation(),
798 : IntPtrConstant(StoreHandler::kFirstPrototypeIndex));
799 :
800 155 : Comment("array_handler");
801 155 : Label can_access(this);
802 : // Only Tuple3 handlers are allowed to have code handlers.
803 : CSA_ASSERT(this, TaggedIsSmi(smi_or_code));
804 : GotoIfNot(
805 : IsSetSmi(smi_or_code, StoreHandler::DoAccessCheckOnReceiverBits::kMask),
806 310 : &can_access);
807 :
808 : {
809 : // Skip this entry of a handler.
810 : var_start_index.Bind(
811 310 : IntPtrConstant(StoreHandler::kFirstPrototypeIndex + 1));
812 :
813 : int offset =
814 : FixedArray::OffsetOfElementAt(StoreHandler::kFirstPrototypeIndex);
815 : Node* expected_native_context =
816 155 : LoadWeakCellValue(LoadObjectField(handler, offset), miss);
817 :
818 : EmitAccessCheck(expected_native_context, p->context, p->receiver,
819 155 : &can_access, miss);
820 : }
821 155 : BIND(&can_access);
822 :
823 310 : Node* length = SmiUntag(maybe_transition_cell);
824 : BuildFastLoop(var_start_index.value(), length,
825 155 : [=](Node* current) {
826 : Node* prototype_cell =
827 155 : LoadFixedArrayElement(handler, current);
828 155 : CheckPrototype(prototype_cell, p->name, miss);
829 155 : },
830 310 : 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
831 :
832 : Node* maybe_transition_cell = LoadFixedArrayElement(
833 155 : handler, StoreHandler::kTransitionMapOrHolderCellIndex);
834 155 : Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
835 155 : var_transition_map_or_holder.Bind(transition);
836 310 : Goto(&do_store);
837 : }
838 :
839 155 : BIND(&do_store);
840 : {
841 155 : Node* transition = var_transition_map_or_holder.value();
842 310 : Branch(IsMap(transition), &if_transition_map, &if_holder_object);
843 : }
844 :
845 155 : BIND(&if_transition_map);
846 : {
847 155 : Label if_transition_to_constant(this), if_store_normal(this);
848 :
849 155 : Node* holder = p->receiver;
850 155 : Node* transition_map = var_transition_map_or_holder.value();
851 :
852 310 : GotoIf(IsDeprecatedMap(transition_map), miss);
853 :
854 155 : if (support_elements == kSupportElements) {
855 : Label if_smi_handler(this);
856 :
857 62 : GotoIf(TaggedIsSmi(smi_or_code), &if_smi_handler);
858 : Node* code_handler = smi_or_code;
859 : CSA_ASSERT(this, IsCodeMap(LoadMap(code_handler)));
860 :
861 31 : StoreTransitionDescriptor descriptor(isolate());
862 : TailCallStub(descriptor, code_handler, p->context, p->receiver, p->name,
863 31 : transition_map, p->value, p->slot, p->vector);
864 :
865 62 : BIND(&if_smi_handler);
866 : }
867 :
868 : Node* smi_handler = smi_or_code;
869 : CSA_ASSERT(this, TaggedIsSmi(smi_handler));
870 310 : Node* handler_word = SmiUntag(smi_handler);
871 :
872 : Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
873 310 : GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kStoreNormal)),
874 310 : &if_store_normal);
875 : GotoIf(WordEqual(handler_kind,
876 310 : IntPtrConstant(StoreHandler::kTransitionToConstant)),
877 310 : &if_transition_to_constant);
878 : CSA_ASSERT(this,
879 : WordEqual(handler_kind,
880 : IntPtrConstant(StoreHandler::kTransitionToField)));
881 :
882 : // Handle transitioning field stores.
883 : HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition_map,
884 155 : miss);
885 :
886 155 : BIND(&if_transition_to_constant);
887 : {
888 : // Check that constant matches value.
889 : Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
890 : Node* scaled_descriptor =
891 465 : IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
892 : Node* value_index =
893 : IntPtrAdd(scaled_descriptor,
894 : IntPtrConstant(DescriptorArray::kFirstIndex +
895 465 : DescriptorArray::kEntryValueIndex));
896 310 : Node* descriptors = LoadMapDescriptors(transition_map);
897 : CSA_ASSERT(
898 : this, UintPtrLessThan(descriptor,
899 : LoadAndUntagFixedArrayBaseLength(descriptors)));
900 :
901 155 : Node* constant = LoadFixedArrayElement(descriptors, value_index);
902 465 : GotoIf(WordNotEqual(p->value, constant), miss);
903 :
904 155 : StoreMap(p->receiver, transition_map);
905 310 : Return(p->value);
906 : }
907 :
908 155 : BIND(&if_store_normal);
909 : {
910 465 : Node* properties = LoadSlowProperties(p->receiver);
911 :
912 155 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
913 155 : Label found(this, &var_name_index), not_found(this);
914 : NameDictionaryLookup<NameDictionary>(properties, p->name, &found,
915 155 : &var_name_index, ¬_found);
916 155 : BIND(&found);
917 : {
918 : Node* details = LoadDetailsByKeyIndex<NameDictionary>(
919 155 : properties, var_name_index.value());
920 : // Check that the property is a writable data property (no accessor).
921 : const int kTypeAndReadOnlyMask =
922 : PropertyDetails::KindField::kMask |
923 : PropertyDetails::kAttributesReadOnlyMask;
924 : STATIC_ASSERT(kData == 0);
925 310 : GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
926 :
927 : StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(),
928 155 : p->value);
929 310 : Return(p->value);
930 : }
931 :
932 155 : BIND(¬_found);
933 : {
934 : Label slow(this);
935 155 : Add<NameDictionary>(properties, p->name, p->value, &slow);
936 310 : Return(p->value);
937 :
938 155 : BIND(&slow);
939 : TailCallRuntime(Runtime::kAddDictionaryProperty, p->context,
940 310 : p->receiver, p->name, p->value);
941 155 : }
942 155 : }
943 : }
944 155 : BIND(&if_holder_object);
945 : {
946 : Label if_store_global_proxy(this);
947 155 : Node* holder = var_transition_map_or_holder.value();
948 :
949 : Node* smi_handler = smi_or_code;
950 : CSA_ASSERT(this, TaggedIsSmi(smi_handler));
951 310 : Node* handler_word = SmiUntag(smi_handler);
952 :
953 : Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
954 : GotoIf(WordEqual(handler_kind,
955 310 : IntPtrConstant(StoreHandler::kStoreGlobalProxy)),
956 310 : &if_store_global_proxy);
957 : CSA_ASSERT(this,
958 : WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)));
959 155 : HandleStoreToProxy(p, holder, miss, support_elements);
960 :
961 155 : BIND(&if_store_global_proxy);
962 : {
963 : ExitPoint direct_exit(this);
964 155 : StoreGlobalIC_PropertyCellCase(holder, p->value, &direct_exit, miss);
965 155 : }
966 155 : }
967 155 : }
968 :
969 310 : void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p,
970 : Node* proxy, Label* miss,
971 : ElementSupport support_elements) {
972 310 : VARIABLE(var_index, MachineType::PointerRepresentation());
973 620 : VARIABLE(var_unique, MachineRepresentation::kTagged);
974 930 : VARIABLE(var_language_mode, MachineRepresentation::kTaggedSigned,
975 : SmiConstant(Smi::FromEnum(LanguageMode::kStrict)));
976 :
977 310 : Label if_index(this), if_unique_name(this), language_mode_determined(this),
978 310 : to_name_failed(this, Label::kDeferred);
979 310 : BranchIfStrictMode(p->vector, p->slot, &language_mode_determined);
980 620 : var_language_mode.Bind(SmiConstant(Smi::FromEnum(LanguageMode::kSloppy)));
981 310 : Goto(&language_mode_determined);
982 310 : BIND(&language_mode_determined);
983 :
984 310 : if (support_elements == kSupportElements) {
985 : TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
986 62 : &to_name_failed);
987 :
988 62 : BIND(&if_unique_name);
989 : CallBuiltin(Builtins::kProxySetProperty, p->context, proxy,
990 : var_unique.value(), p->value, p->receiver,
991 62 : var_language_mode.value());
992 124 : Return(p->value);
993 :
994 : // The index case is handled earlier by the runtime.
995 62 : BIND(&if_index);
996 : // TODO(mslekova): introduce TryToName that doesn't try to compute
997 : // the intptr index value
998 62 : Goto(&to_name_failed);
999 :
1000 62 : BIND(&to_name_failed);
1001 : TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy,
1002 62 : p->name, p->value, p->receiver, var_language_mode.value());
1003 : } else {
1004 248 : Node* name = ToName(p->context, p->name);
1005 : TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name,
1006 248 : p->value, p->receiver, var_language_mode.value());
1007 310 : }
1008 310 : }
1009 :
1010 310 : void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
1011 : Node* holder, Node* value,
1012 : Node* transition,
1013 : Label* miss) {
1014 310 : Comment(transition ? "transitioning field store" : "field store");
1015 :
1016 : #ifdef DEBUG
1017 : Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
1018 : if (transition) {
1019 : CSA_ASSERT(
1020 : this,
1021 : Word32Or(
1022 : WordEqual(handler_kind,
1023 : IntPtrConstant(StoreHandler::kTransitionToField)),
1024 : WordEqual(handler_kind,
1025 : IntPtrConstant(StoreHandler::kTransitionToConstant))));
1026 : } else {
1027 : if (FLAG_track_constant_fields) {
1028 : CSA_ASSERT(
1029 : this,
1030 : Word32Or(WordEqual(handler_kind,
1031 : IntPtrConstant(StoreHandler::kStoreField)),
1032 : WordEqual(handler_kind,
1033 : IntPtrConstant(StoreHandler::kStoreConstField))));
1034 : } else {
1035 : CSA_ASSERT(this, WordEqual(handler_kind,
1036 : IntPtrConstant(StoreHandler::kStoreField)));
1037 : }
1038 : }
1039 : #endif
1040 :
1041 : Node* field_representation =
1042 310 : DecodeWord<StoreHandler::FieldRepresentationBits>(handler_word);
1043 :
1044 310 : Label if_smi_field(this), if_double_field(this), if_heap_object_field(this),
1045 310 : if_tagged_field(this);
1046 :
1047 620 : GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kTagged)),
1048 620 : &if_tagged_field);
1049 : GotoIf(WordEqual(field_representation,
1050 620 : IntPtrConstant(StoreHandler::kHeapObject)),
1051 620 : &if_heap_object_field);
1052 620 : GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kDouble)),
1053 620 : &if_double_field);
1054 : CSA_ASSERT(this, WordEqual(field_representation,
1055 : IntPtrConstant(StoreHandler::kSmi)));
1056 310 : Goto(&if_smi_field);
1057 :
1058 310 : BIND(&if_tagged_field);
1059 : {
1060 310 : Comment("store tagged field");
1061 : HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
1062 310 : value, transition, miss);
1063 : }
1064 :
1065 310 : BIND(&if_double_field);
1066 : {
1067 310 : Comment("store double field");
1068 : HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
1069 310 : value, transition, miss);
1070 : }
1071 :
1072 310 : BIND(&if_heap_object_field);
1073 : {
1074 310 : Comment("store heap object field");
1075 : HandleStoreFieldAndReturn(handler_word, holder,
1076 : Representation::HeapObject(), value, transition,
1077 310 : miss);
1078 : }
1079 :
1080 310 : BIND(&if_smi_field);
1081 : {
1082 310 : Comment("store smi field");
1083 : HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
1084 310 : value, transition, miss);
1085 310 : }
1086 310 : }
1087 :
1088 1240 : void AccessorAssembler::HandleStoreFieldAndReturn(Node* handler_word,
1089 : Node* holder,
1090 : Representation representation,
1091 : Node* value, Node* transition,
1092 : Label* miss) {
1093 1240 : bool transition_to_field = transition != nullptr;
1094 : Node* prepared_value = PrepareValueForStore(
1095 1240 : handler_word, holder, representation, transition, value, miss);
1096 :
1097 2480 : Label if_inobject(this), if_out_of_object(this);
1098 : Branch(IsSetWord<StoreHandler::IsInobjectBits>(handler_word), &if_inobject,
1099 2480 : &if_out_of_object);
1100 :
1101 1240 : BIND(&if_inobject);
1102 : {
1103 : StoreNamedField(handler_word, holder, true, representation, prepared_value,
1104 1240 : transition_to_field, miss);
1105 1240 : if (transition_to_field) {
1106 620 : StoreMap(holder, transition);
1107 : }
1108 1240 : Return(value);
1109 : }
1110 :
1111 1240 : BIND(&if_out_of_object);
1112 : {
1113 1240 : if (transition_to_field) {
1114 620 : ExtendPropertiesBackingStore(holder, handler_word);
1115 : }
1116 :
1117 : StoreNamedField(handler_word, holder, false, representation, prepared_value,
1118 1240 : transition_to_field, miss);
1119 1240 : if (transition_to_field) {
1120 620 : StoreMap(holder, transition);
1121 : }
1122 1240 : Return(value);
1123 1240 : }
1124 1240 : }
1125 :
1126 1240 : Node* AccessorAssembler::PrepareValueForStore(Node* handler_word, Node* holder,
1127 : Representation representation,
1128 : Node* transition, Node* value,
1129 : Label* bailout) {
1130 1240 : if (representation.IsDouble()) {
1131 310 : value = TryTaggedToFloat64(value, bailout);
1132 :
1133 930 : } else if (representation.IsHeapObject()) {
1134 620 : GotoIf(TaggedIsSmi(value), bailout);
1135 :
1136 : Label done(this);
1137 : if (FLAG_track_constant_fields && !transition) {
1138 : // Skip field type check in favor of constant value check when storing
1139 : // to constant field.
1140 : GotoIf(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
1141 : IntPtrConstant(StoreHandler::kStoreConstField)),
1142 : &done);
1143 : }
1144 : Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
1145 : Node* scaled_descriptor =
1146 930 : IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
1147 : Node* value_index =
1148 : IntPtrAdd(scaled_descriptor,
1149 : IntPtrConstant(DescriptorArray::kFirstIndex +
1150 930 : DescriptorArray::kEntryValueIndex));
1151 : Node* descriptors =
1152 1085 : LoadMapDescriptors(transition ? transition : LoadMap(holder));
1153 : CSA_ASSERT(this,
1154 : UintPtrLessThan(descriptor,
1155 : LoadAndUntagFixedArrayBaseLength(descriptors)));
1156 310 : Node* maybe_field_type = LoadFixedArrayElement(descriptors, value_index);
1157 :
1158 620 : GotoIf(TaggedIsSmi(maybe_field_type), &done);
1159 : // Check that value type matches the field type.
1160 : {
1161 310 : Node* field_type = LoadWeakCellValue(maybe_field_type, bailout);
1162 620 : Branch(WordEqual(LoadMap(value), field_type), &done, bailout);
1163 : }
1164 310 : BIND(&done);
1165 :
1166 620 : } else if (representation.IsSmi()) {
1167 620 : GotoIfNot(TaggedIsSmi(value), bailout);
1168 :
1169 : } else {
1170 : DCHECK(representation.IsTagged());
1171 : }
1172 1240 : return value;
1173 : }
1174 :
1175 620 : void AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
1176 : Node* handler_word) {
1177 620 : Label done(this);
1178 1240 : GotoIfNot(IsSetWord<StoreHandler::ExtendStorageBits>(handler_word), &done);
1179 620 : Comment("[ Extend storage");
1180 :
1181 : ParameterMode mode = OptimalParameterMode();
1182 :
1183 : // TODO(gsathya): Clean up the type conversions by creating smarter
1184 : // helpers that do the correct op based on the mode.
1185 1240 : VARIABLE(var_properties, MachineRepresentation::kTaggedPointer);
1186 1240 : VARIABLE(var_encoded_hash, MachineRepresentation::kWord32);
1187 1240 : VARIABLE(var_length, ParameterRepresentation(mode));
1188 :
1189 : Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset);
1190 620 : var_properties.Bind(properties);
1191 :
1192 620 : Label if_smi_hash(this), if_property_array(this), extend_store(this);
1193 1240 : Branch(TaggedIsSmi(properties), &if_smi_hash, &if_property_array);
1194 :
1195 620 : BIND(&if_smi_hash);
1196 : {
1197 1240 : Node* hash = SmiToWord32(properties);
1198 : Node* encoded_hash =
1199 1860 : Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift));
1200 620 : var_encoded_hash.Bind(encoded_hash);
1201 620 : var_length.Bind(IntPtrOrSmiConstant(0, mode));
1202 1240 : var_properties.Bind(EmptyFixedArrayConstant());
1203 620 : Goto(&extend_store);
1204 : }
1205 :
1206 620 : BIND(&if_property_array);
1207 : {
1208 : Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField(
1209 1240 : var_properties.value(), PropertyArray::kLengthAndHashOffset);
1210 : var_encoded_hash.Bind(Word32And(
1211 1860 : length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask)));
1212 : Node* length_intptr = ChangeInt32ToIntPtr(
1213 : Word32And(length_and_hash_int32,
1214 2480 : Int32Constant(PropertyArray::LengthField::kMask)));
1215 : Node* length = WordToParameter(length_intptr, mode);
1216 620 : var_length.Bind(length);
1217 620 : Goto(&extend_store);
1218 : }
1219 :
1220 620 : BIND(&extend_store);
1221 : {
1222 : // Previous property deletion could have left behind unused backing store
1223 : // capacity even for a map that think it doesn't have any unused fields.
1224 : // Perform a bounds check to see if we actually have to grow the array.
1225 : Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
1226 : Node* size = ElementOffsetFromIndex(var_length.value(), PACKED_ELEMENTS,
1227 620 : mode, FixedArray::kHeaderSize);
1228 1240 : GotoIf(UintPtrLessThan(offset, size), &done);
1229 :
1230 620 : Node* delta = IntPtrOrSmiConstant(JSObject::kFieldsAdded, mode);
1231 620 : Node* new_capacity = IntPtrOrSmiAdd(var_length.value(), delta, mode);
1232 :
1233 : // Grow properties array.
1234 : DCHECK(kMaxNumberOfDescriptors + JSObject::kFieldsAdded <
1235 : FixedArrayBase::GetMaxLengthForNewSpaceAllocation(PACKED_ELEMENTS));
1236 : // The size of a new properties backing store is guaranteed to be small
1237 : // enough that the new backing store will be allocated in new space.
1238 : CSA_ASSERT(this,
1239 : UintPtrOrSmiLessThan(
1240 : new_capacity,
1241 : IntPtrOrSmiConstant(
1242 : kMaxNumberOfDescriptors + JSObject::kFieldsAdded, mode),
1243 : mode));
1244 :
1245 620 : Node* new_properties = AllocatePropertyArray(new_capacity, mode);
1246 :
1247 : FillPropertyArrayWithUndefined(new_properties, var_length.value(),
1248 620 : new_capacity, mode);
1249 :
1250 : // |new_properties| is guaranteed to be in new space, so we can skip
1251 : // the write barrier.
1252 : CopyPropertyArrayValues(var_properties.value(), new_properties,
1253 620 : var_length.value(), SKIP_WRITE_BARRIER, mode);
1254 :
1255 : // TODO(gsathya): Clean up the type conversions by creating smarter
1256 : // helpers that do the correct op based on the mode.
1257 : Node* new_capacity_int32 =
1258 1240 : TruncateWordToWord32(ParameterToWord(new_capacity, mode));
1259 : Node* new_length_and_hash_int32 =
1260 1860 : Word32Or(var_encoded_hash.value(), new_capacity_int32);
1261 : StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset,
1262 1240 : SmiFromWord32(new_length_and_hash_int32));
1263 620 : StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties);
1264 620 : Comment("] Extend storage");
1265 620 : Goto(&done);
1266 : }
1267 1240 : BIND(&done);
1268 620 : }
1269 :
1270 2480 : void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object,
1271 : bool is_inobject,
1272 : Representation representation,
1273 : Node* value, bool transition_to_field,
1274 : Label* bailout) {
1275 : bool store_value_as_double = representation.IsDouble();
1276 : Node* property_storage = object;
1277 2480 : if (!is_inobject) {
1278 2480 : property_storage = LoadFastProperties(object);
1279 : }
1280 :
1281 2480 : Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
1282 2480 : if (representation.IsDouble()) {
1283 620 : if (!FLAG_unbox_double_fields || !is_inobject) {
1284 310 : if (transition_to_field) {
1285 310 : Node* heap_number = AllocateHeapNumberWithValue(value, MUTABLE);
1286 : // Store the new mutable heap number into the object.
1287 : value = heap_number;
1288 : store_value_as_double = false;
1289 : } else {
1290 : // Load the heap number.
1291 : property_storage = LoadObjectField(property_storage, offset);
1292 : // Store the double value into it.
1293 310 : offset = IntPtrConstant(HeapNumber::kValueOffset);
1294 : }
1295 : }
1296 : }
1297 :
1298 : // Do constant value check if necessary.
1299 : if (FLAG_track_constant_fields && !transition_to_field) {
1300 : Label done(this);
1301 : GotoIfNot(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
1302 : IntPtrConstant(StoreHandler::kStoreConstField)),
1303 : &done);
1304 : {
1305 : if (store_value_as_double) {
1306 : Node* current_value =
1307 : LoadObjectField(property_storage, offset, MachineType::Float64());
1308 : GotoIfNot(Float64Equal(current_value, value), bailout);
1309 : } else {
1310 : Node* current_value = LoadObjectField(property_storage, offset);
1311 : GotoIfNot(WordEqual(current_value, value), bailout);
1312 : }
1313 : Goto(&done);
1314 : }
1315 : BIND(&done);
1316 : }
1317 :
1318 : // Do the store.
1319 2480 : if (store_value_as_double) {
1320 : StoreObjectFieldNoWriteBarrier(property_storage, offset, value,
1321 465 : MachineRepresentation::kFloat64);
1322 2015 : } else if (representation.IsSmi()) {
1323 620 : StoreObjectFieldNoWriteBarrier(property_storage, offset, value);
1324 : } else {
1325 1395 : StoreObjectField(property_storage, offset, value);
1326 : }
1327 2480 : }
1328 :
1329 62 : void AccessorAssembler::EmitFastElementsBoundsCheck(Node* object,
1330 : Node* elements,
1331 : Node* intptr_index,
1332 : Node* is_jsarray_condition,
1333 : Label* miss) {
1334 62 : VARIABLE(var_length, MachineType::PointerRepresentation());
1335 62 : Comment("Fast elements bounds check");
1336 62 : Label if_array(this), length_loaded(this, &var_length);
1337 62 : GotoIf(is_jsarray_condition, &if_array);
1338 : {
1339 186 : var_length.Bind(SmiUntag(LoadFixedArrayBaseLength(elements)));
1340 62 : Goto(&length_loaded);
1341 : }
1342 62 : BIND(&if_array);
1343 : {
1344 186 : var_length.Bind(SmiUntag(LoadFastJSArrayLength(object)));
1345 62 : Goto(&length_loaded);
1346 : }
1347 62 : BIND(&length_loaded);
1348 248 : GotoIfNot(UintPtrLessThan(intptr_index, var_length.value()), miss);
1349 62 : }
1350 :
1351 62 : void AccessorAssembler::EmitElementLoad(
1352 : Node* object, Node* elements, Node* elements_kind, Node* intptr_index,
1353 : Node* is_jsarray_condition, Label* if_hole, Label* rebox_double,
1354 : Variable* var_double_value, Label* unimplemented_elements_kind,
1355 : Label* out_of_bounds, Label* miss, ExitPoint* exit_point) {
1356 124 : Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this),
1357 62 : if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
1358 62 : if_dictionary(this);
1359 : GotoIf(
1360 124 : Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
1361 124 : &if_nonfast);
1362 :
1363 : EmitFastElementsBoundsCheck(object, elements, intptr_index,
1364 62 : is_jsarray_condition, out_of_bounds);
1365 : int32_t kinds[] = {// Handled by if_fast_packed.
1366 : PACKED_SMI_ELEMENTS, PACKED_ELEMENTS,
1367 : // Handled by if_fast_holey.
1368 : HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
1369 : // Handled by if_fast_double.
1370 : PACKED_DOUBLE_ELEMENTS,
1371 : // Handled by if_fast_holey_double.
1372 62 : HOLEY_DOUBLE_ELEMENTS};
1373 : Label* labels[] = {// FAST_{SMI,}_ELEMENTS
1374 : &if_fast_packed, &if_fast_packed,
1375 : // FAST_HOLEY_{SMI,}_ELEMENTS
1376 : &if_fast_holey, &if_fast_holey,
1377 : // PACKED_DOUBLE_ELEMENTS
1378 : &if_fast_double,
1379 : // HOLEY_DOUBLE_ELEMENTS
1380 62 : &if_fast_holey_double};
1381 : Switch(elements_kind, unimplemented_elements_kind, kinds, labels,
1382 62 : arraysize(kinds));
1383 :
1384 62 : BIND(&if_fast_packed);
1385 : {
1386 62 : Comment("fast packed elements");
1387 62 : exit_point->Return(LoadFixedArrayElement(elements, intptr_index));
1388 : }
1389 :
1390 62 : BIND(&if_fast_holey);
1391 : {
1392 62 : Comment("fast holey elements");
1393 62 : Node* element = LoadFixedArrayElement(elements, intptr_index);
1394 124 : GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
1395 62 : exit_point->Return(element);
1396 : }
1397 :
1398 62 : BIND(&if_fast_double);
1399 : {
1400 62 : Comment("packed double elements");
1401 : var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index,
1402 62 : MachineType::Float64()));
1403 62 : Goto(rebox_double);
1404 : }
1405 :
1406 62 : BIND(&if_fast_holey_double);
1407 : {
1408 62 : Comment("holey double elements");
1409 : Node* value = LoadFixedDoubleArrayElement(elements, intptr_index,
1410 : MachineType::Float64(), 0,
1411 62 : INTPTR_PARAMETERS, if_hole);
1412 62 : var_double_value->Bind(value);
1413 62 : Goto(rebox_double);
1414 : }
1415 :
1416 62 : BIND(&if_nonfast);
1417 : {
1418 : STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
1419 : GotoIf(Int32GreaterThanOrEqual(
1420 : elements_kind,
1421 124 : Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
1422 124 : &if_typed_array);
1423 124 : GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
1424 124 : &if_dictionary);
1425 62 : Goto(unimplemented_elements_kind);
1426 : }
1427 :
1428 62 : BIND(&if_dictionary);
1429 : {
1430 62 : Comment("dictionary elements");
1431 186 : GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
1432 62 : VARIABLE(var_entry, MachineType::PointerRepresentation());
1433 62 : Label if_found(this);
1434 : NumberDictionaryLookup<SeededNumberDictionary>(
1435 62 : elements, intptr_index, &if_found, &var_entry, if_hole);
1436 62 : BIND(&if_found);
1437 : // Check that the value is a data property.
1438 62 : Node* index = EntryToIndex<SeededNumberDictionary>(var_entry.value());
1439 : Node* details =
1440 : LoadDetailsByKeyIndex<SeededNumberDictionary>(elements, index);
1441 : Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
1442 : // TODO(jkummerow): Support accessors without missing?
1443 186 : GotoIfNot(Word32Equal(kind, Int32Constant(kData)), miss);
1444 : // Finally, load the value.
1445 : exit_point->Return(
1446 124 : LoadValueByKeyIndex<SeededNumberDictionary>(elements, index));
1447 : }
1448 :
1449 62 : BIND(&if_typed_array);
1450 : {
1451 62 : Comment("typed elements");
1452 : // Check if buffer has been neutered.
1453 : Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
1454 124 : GotoIf(IsDetachedBuffer(buffer), miss);
1455 :
1456 : // Bounds check.
1457 : Node* length =
1458 124 : SmiUntag(CAST(LoadObjectField(object, JSTypedArray::kLengthOffset)));
1459 124 : GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds);
1460 :
1461 : // Backing store = external_pointer + base_pointer.
1462 : Node* external_pointer =
1463 : LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
1464 62 : MachineType::Pointer());
1465 : Node* base_pointer =
1466 : LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
1467 : Node* backing_store =
1468 186 : IntPtrAdd(external_pointer, BitcastTaggedToWord(base_pointer));
1469 :
1470 62 : Label uint8_elements(this), int8_elements(this), uint16_elements(this),
1471 62 : int16_elements(this), uint32_elements(this), int32_elements(this),
1472 62 : float32_elements(this), float64_elements(this);
1473 : Label* elements_kind_labels[] = {
1474 : &uint8_elements, &uint8_elements, &int8_elements,
1475 : &uint16_elements, &int16_elements, &uint32_elements,
1476 62 : &int32_elements, &float32_elements, &float64_elements};
1477 : int32_t elements_kinds[] = {
1478 : UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
1479 : UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
1480 62 : INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS};
1481 : const size_t kTypedElementsKindCount =
1482 : LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
1483 : FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
1484 : DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
1485 : DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
1486 : Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
1487 62 : kTypedElementsKindCount);
1488 62 : BIND(&uint8_elements);
1489 : {
1490 62 : Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
1491 62 : Node* element = Load(MachineType::Uint8(), backing_store, intptr_index);
1492 124 : exit_point->Return(SmiFromWord32(element));
1493 : }
1494 62 : BIND(&int8_elements);
1495 : {
1496 62 : Comment("INT8_ELEMENTS");
1497 62 : Node* element = Load(MachineType::Int8(), backing_store, intptr_index);
1498 124 : exit_point->Return(SmiFromWord32(element));
1499 : }
1500 62 : BIND(&uint16_elements);
1501 : {
1502 62 : Comment("UINT16_ELEMENTS");
1503 186 : Node* index = WordShl(intptr_index, IntPtrConstant(1));
1504 62 : Node* element = Load(MachineType::Uint16(), backing_store, index);
1505 124 : exit_point->Return(SmiFromWord32(element));
1506 : }
1507 62 : BIND(&int16_elements);
1508 : {
1509 62 : Comment("INT16_ELEMENTS");
1510 186 : Node* index = WordShl(intptr_index, IntPtrConstant(1));
1511 62 : Node* element = Load(MachineType::Int16(), backing_store, index);
1512 124 : exit_point->Return(SmiFromWord32(element));
1513 : }
1514 62 : BIND(&uint32_elements);
1515 : {
1516 62 : Comment("UINT32_ELEMENTS");
1517 186 : Node* index = WordShl(intptr_index, IntPtrConstant(2));
1518 62 : Node* element = Load(MachineType::Uint32(), backing_store, index);
1519 124 : exit_point->Return(ChangeUint32ToTagged(element));
1520 : }
1521 62 : BIND(&int32_elements);
1522 : {
1523 62 : Comment("INT32_ELEMENTS");
1524 186 : Node* index = WordShl(intptr_index, IntPtrConstant(2));
1525 62 : Node* element = Load(MachineType::Int32(), backing_store, index);
1526 124 : exit_point->Return(ChangeInt32ToTagged(element));
1527 : }
1528 62 : BIND(&float32_elements);
1529 : {
1530 62 : Comment("FLOAT32_ELEMENTS");
1531 186 : Node* index = WordShl(intptr_index, IntPtrConstant(2));
1532 62 : Node* element = Load(MachineType::Float32(), backing_store, index);
1533 124 : var_double_value->Bind(ChangeFloat32ToFloat64(element));
1534 62 : Goto(rebox_double);
1535 : }
1536 62 : BIND(&float64_elements);
1537 : {
1538 62 : Comment("FLOAT64_ELEMENTS");
1539 186 : Node* index = WordShl(intptr_index, IntPtrConstant(3));
1540 62 : Node* element = Load(MachineType::Float64(), backing_store, index);
1541 62 : var_double_value->Bind(element);
1542 62 : Goto(rebox_double);
1543 62 : }
1544 62 : }
1545 62 : }
1546 :
1547 217 : void AccessorAssembler::CheckPrototype(Node* prototype_cell, Node* name,
1548 : Label* miss) {
1549 217 : Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
1550 :
1551 217 : Label done(this);
1552 217 : Label if_property_cell(this), if_dictionary_object(this);
1553 :
1554 : // |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
1555 : Branch(IsPropertyCell(maybe_prototype), &if_property_cell,
1556 434 : &if_dictionary_object);
1557 :
1558 217 : BIND(&if_dictionary_object);
1559 : {
1560 : CSA_ASSERT(this, IsDictionaryMap(LoadMap(maybe_prototype)));
1561 217 : NameDictionaryNegativeLookup(maybe_prototype, name, miss);
1562 217 : Goto(&done);
1563 : }
1564 :
1565 217 : BIND(&if_property_cell);
1566 : {
1567 : // Ensure the property cell still contains the hole.
1568 : Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
1569 434 : GotoIfNot(IsTheHole(value), miss);
1570 217 : Goto(&done);
1571 : }
1572 :
1573 434 : BIND(&done);
1574 217 : }
1575 :
1576 217 : void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, Node* name,
1577 : Label* miss) {
1578 : CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
1579 434 : Node* properties = LoadSlowProperties(object);
1580 : // Ensure the property does not exist in a dictionary-mode object.
1581 217 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
1582 217 : Label done(this);
1583 : NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index,
1584 217 : &done);
1585 434 : BIND(&done);
1586 217 : }
1587 :
1588 465 : void AccessorAssembler::BranchIfStrictMode(Node* vector, Node* slot,
1589 : Label* if_strict) {
1590 : Node* sfi =
1591 465 : LoadObjectField(vector, FeedbackVector::kSharedFunctionInfoOffset);
1592 : Node* metadata =
1593 : LoadObjectField(sfi, SharedFunctionInfo::kFeedbackMetadataOffset);
1594 930 : Node* slot_int = SmiToWord32(slot);
1595 :
1596 : // See VectorICComputer::index().
1597 : const int kItemsPerWord = FeedbackMetadata::VectorICComputer::kItemsPerWord;
1598 1395 : Node* word_index = Int32Div(slot_int, Int32Constant(kItemsPerWord));
1599 1395 : Node* word_offset = Int32Mod(slot_int, Int32Constant(kItemsPerWord));
1600 : Node* data = SmiToWord32(LoadFixedArrayElement(
1601 465 : metadata, ChangeInt32ToIntPtr(word_index),
1602 1860 : FeedbackMetadata::kReservedIndexCount * kPointerSize, INTPTR_PARAMETERS));
1603 : // See VectorICComputer::decode().
1604 : const int kBitsPerItem = FeedbackMetadata::kFeedbackSlotKindBits;
1605 1395 : Node* shift = Int32Mul(word_offset, Int32Constant(kBitsPerItem));
1606 : const int kMask = FeedbackMetadata::VectorICComputer::kMask;
1607 1860 : Node* kind = Word32And(Word32Shr(data, shift), Int32Constant(kMask));
1608 :
1609 : STATIC_ASSERT(FeedbackSlotKind::kStoreGlobalSloppy <=
1610 : FeedbackSlotKind::kLastSloppyKind);
1611 : STATIC_ASSERT(FeedbackSlotKind::kStoreKeyedSloppy <=
1612 : FeedbackSlotKind::kLastSloppyKind);
1613 : STATIC_ASSERT(FeedbackSlotKind::kStoreNamedSloppy <=
1614 : FeedbackSlotKind::kLastSloppyKind);
1615 : GotoIfNot(Int32LessThanOrEqual(kind, Int32Constant(static_cast<int>(
1616 930 : FeedbackSlotKind::kLastSloppyKind))),
1617 930 : if_strict);
1618 465 : }
1619 :
1620 31 : void AccessorAssembler::GenericElementLoad(Node* receiver, Node* receiver_map,
1621 : Node* instance_type, Node* index,
1622 : Label* slow) {
1623 31 : Comment("integer index");
1624 :
1625 31 : ExitPoint direct_exit(this);
1626 :
1627 31 : Label if_element_hole(this), if_oob(this);
1628 : // Receivers requiring non-standard element accesses (interceptors, access
1629 : // checks, strings and string wrappers, proxies) are handled in the runtime.
1630 : GotoIf(Int32LessThanOrEqual(instance_type,
1631 62 : Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
1632 62 : slow);
1633 62 : Node* elements = LoadElements(receiver);
1634 62 : Node* elements_kind = LoadMapElementsKind(receiver_map);
1635 31 : Node* is_jsarray_condition = InstanceTypeEqual(instance_type, JS_ARRAY_TYPE);
1636 62 : VARIABLE(var_double_value, MachineRepresentation::kFloat64);
1637 31 : Label rebox_double(this, &var_double_value);
1638 :
1639 : // Unimplemented elements kinds fall back to a runtime call.
1640 : Label* unimplemented_elements_kind = slow;
1641 62 : IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
1642 : EmitElementLoad(receiver, elements, elements_kind, index,
1643 : is_jsarray_condition, &if_element_hole, &rebox_double,
1644 : &var_double_value, unimplemented_elements_kind, &if_oob, slow,
1645 31 : &direct_exit);
1646 :
1647 31 : BIND(&rebox_double);
1648 93 : Return(AllocateHeapNumberWithValue(var_double_value.value()));
1649 :
1650 31 : BIND(&if_oob);
1651 : {
1652 31 : Comment("out of bounds");
1653 : // Negative keys can't take the fast OOB path.
1654 93 : GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), slow);
1655 : // Positive OOB indices are effectively the same as hole loads.
1656 31 : Goto(&if_element_hole);
1657 : }
1658 :
1659 31 : BIND(&if_element_hole);
1660 : {
1661 31 : Comment("found the hole");
1662 : Label return_undefined(this);
1663 31 : BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, slow);
1664 :
1665 31 : BIND(&return_undefined);
1666 62 : Return(UndefinedConstant());
1667 31 : }
1668 31 : }
1669 :
1670 62 : void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
1671 : Node* instance_type,
1672 : const LoadICParameters* p,
1673 : Label* slow,
1674 : UseStubCache use_stub_cache) {
1675 62 : ExitPoint direct_exit(this);
1676 :
1677 62 : Comment("key is unique name");
1678 62 : Label if_found_on_receiver(this), if_property_dictionary(this),
1679 62 : lookup_prototype_chain(this), special_receiver(this);
1680 124 : VARIABLE(var_details, MachineRepresentation::kWord32);
1681 124 : VARIABLE(var_value, MachineRepresentation::kTagged);
1682 :
1683 : // Receivers requiring non-standard accesses (interceptors, access
1684 : // checks, strings and string wrappers) are handled in the runtime.
1685 : GotoIf(Int32LessThanOrEqual(instance_type,
1686 124 : Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
1687 124 : &special_receiver);
1688 :
1689 : // Check if the receiver has fast or slow properties.
1690 124 : Node* bitfield3 = LoadMapBitField3(receiver_map);
1691 62 : GotoIf(IsSetWord32<Map::DictionaryMap>(bitfield3), &if_property_dictionary);
1692 :
1693 : // Try looking up the property on the receiver; if unsuccessful, look
1694 : // for a handler in the stub cache.
1695 124 : Node* descriptors = LoadMapDescriptors(receiver_map);
1696 :
1697 62 : Label if_descriptor_found(this), stub_cache(this);
1698 124 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
1699 : Label* notfound =
1700 62 : use_stub_cache == kUseStubCache ? &stub_cache : &lookup_prototype_chain;
1701 : DescriptorLookup(p->name, descriptors, bitfield3, &if_descriptor_found,
1702 62 : &var_name_index, notfound);
1703 :
1704 62 : BIND(&if_descriptor_found);
1705 : {
1706 : LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
1707 : var_name_index.value(), &var_details,
1708 62 : &var_value);
1709 62 : Goto(&if_found_on_receiver);
1710 : }
1711 :
1712 62 : if (use_stub_cache == kUseStubCache) {
1713 31 : BIND(&stub_cache);
1714 31 : Comment("stub cache probe for fast property load");
1715 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
1716 31 : Label found_handler(this, &var_handler), stub_cache_miss(this);
1717 : TryProbeStubCache(isolate()->load_stub_cache(), receiver, p->name,
1718 31 : &found_handler, &var_handler, &stub_cache_miss);
1719 31 : BIND(&found_handler);
1720 : {
1721 : HandleLoadICHandlerCase(p, var_handler.value(), &stub_cache_miss,
1722 31 : &direct_exit);
1723 : }
1724 :
1725 31 : BIND(&stub_cache_miss);
1726 : {
1727 : // TODO(jkummerow): Check if the property exists on the prototype
1728 : // chain. If it doesn't, then there's no point in missing.
1729 31 : Comment("KeyedLoadGeneric_miss");
1730 : TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
1731 31 : p->name, p->slot, p->vector);
1732 31 : }
1733 : }
1734 :
1735 62 : BIND(&if_property_dictionary);
1736 : {
1737 62 : Comment("dictionary property load");
1738 : // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
1739 : // seeing global objects here (which would need special handling).
1740 :
1741 62 : VARIABLE(var_name_index, MachineType::PointerRepresentation());
1742 62 : Label dictionary_found(this, &var_name_index);
1743 124 : Node* properties = LoadSlowProperties(receiver);
1744 : NameDictionaryLookup<NameDictionary>(properties, p->name, &dictionary_found,
1745 : &var_name_index,
1746 62 : &lookup_prototype_chain);
1747 62 : BIND(&dictionary_found);
1748 : {
1749 : LoadPropertyFromNameDictionary(properties, var_name_index.value(),
1750 62 : &var_details, &var_value);
1751 62 : Goto(&if_found_on_receiver);
1752 62 : }
1753 : }
1754 :
1755 62 : BIND(&if_found_on_receiver);
1756 : {
1757 : Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
1758 62 : p->context, receiver, slow);
1759 124 : IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1);
1760 62 : Return(value);
1761 : }
1762 :
1763 62 : BIND(&lookup_prototype_chain);
1764 : {
1765 62 : VARIABLE(var_holder_map, MachineRepresentation::kTagged);
1766 124 : VARIABLE(var_holder_instance_type, MachineRepresentation::kWord32);
1767 62 : Label return_undefined(this);
1768 62 : Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type};
1769 124 : Label loop(this, arraysize(merged_variables), merged_variables);
1770 :
1771 62 : var_holder_map.Bind(receiver_map);
1772 62 : var_holder_instance_type.Bind(instance_type);
1773 : // Private symbols must not be looked up on the prototype chain.
1774 124 : GotoIf(IsPrivateSymbol(p->name), &return_undefined);
1775 62 : Goto(&loop);
1776 62 : BIND(&loop);
1777 : {
1778 : // Bailout if it can be an integer indexed exotic case.
1779 : GotoIf(InstanceTypeEqual(var_holder_instance_type.value(),
1780 : JS_TYPED_ARRAY_TYPE),
1781 124 : slow);
1782 186 : Node* proto = LoadMapPrototype(var_holder_map.value());
1783 124 : GotoIf(WordEqual(proto, NullConstant()), &return_undefined);
1784 124 : Node* proto_map = LoadMap(proto);
1785 124 : Node* proto_instance_type = LoadMapInstanceType(proto_map);
1786 62 : var_holder_map.Bind(proto_map);
1787 62 : var_holder_instance_type.Bind(proto_instance_type);
1788 62 : Label next_proto(this), return_value(this, &var_value), goto_slow(this);
1789 : TryGetOwnProperty(p->context, receiver, proto, proto_map,
1790 : proto_instance_type, p->name, &return_value, &var_value,
1791 62 : &next_proto, &goto_slow);
1792 :
1793 : // This trampoline and the next are required to appease Turbofan's
1794 : // variable merging.
1795 62 : BIND(&next_proto);
1796 62 : Goto(&loop);
1797 :
1798 62 : BIND(&goto_slow);
1799 62 : Goto(slow);
1800 :
1801 62 : BIND(&return_value);
1802 186 : Return(var_value.value());
1803 : }
1804 :
1805 62 : BIND(&return_undefined);
1806 186 : Return(UndefinedConstant());
1807 : }
1808 :
1809 62 : BIND(&special_receiver);
1810 : {
1811 : // TODO(jkummerow): Consider supporting JSModuleNamespace.
1812 124 : GotoIfNot(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), slow);
1813 :
1814 : direct_exit.ReturnCallStub(
1815 : Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
1816 : p->context, receiver /*holder is the same as receiver*/, p->name,
1817 124 : receiver);
1818 62 : }
1819 62 : }
1820 :
1821 : //////////////////// Stub cache access helpers.
1822 :
1823 : enum AccessorAssembler::StubCacheTable : int {
1824 : kPrimary = static_cast<int>(StubCache::kPrimary),
1825 : kSecondary = static_cast<int>(StubCache::kSecondary)
1826 : };
1827 :
1828 173 : Node* AccessorAssembler::StubCachePrimaryOffset(Node* name, Node* map) {
1829 : // See v8::internal::StubCache::PrimaryOffset().
1830 : STATIC_ASSERT(StubCache::kCacheIndexShift == Name::kHashShift);
1831 : // Compute the hash of the name (use entire hash field).
1832 346 : Node* hash_field = LoadNameHashField(name);
1833 : CSA_ASSERT(this,
1834 : Word32Equal(Word32And(hash_field,
1835 : Int32Constant(Name::kHashNotComputedMask)),
1836 : Int32Constant(0)));
1837 :
1838 : // Using only the low bits in 64-bit mode is unlikely to increase the
1839 : // risk of collision even if the heap is spread over an area larger than
1840 : // 4Gb (and not at all if it isn't).
1841 519 : Node* map32 = TruncateWordToWord32(BitcastTaggedToWord(map));
1842 346 : Node* hash = Int32Add(hash_field, map32);
1843 : // Base the offset on a simple combination of name and map.
1844 519 : hash = Word32Xor(hash, Int32Constant(StubCache::kPrimaryMagic));
1845 : uint32_t mask = (StubCache::kPrimaryTableSize - 1)
1846 : << StubCache::kCacheIndexShift;
1847 692 : return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
1848 : }
1849 :
1850 167 : Node* AccessorAssembler::StubCacheSecondaryOffset(Node* name, Node* seed) {
1851 : // See v8::internal::StubCache::SecondaryOffset().
1852 :
1853 : // Use the seed from the primary cache in the secondary cache.
1854 501 : Node* name32 = TruncateWordToWord32(BitcastTaggedToWord(name));
1855 501 : Node* hash = Int32Sub(TruncateWordToWord32(seed), name32);
1856 501 : hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
1857 : int32_t mask = (StubCache::kSecondaryTableSize - 1)
1858 : << StubCache::kCacheIndexShift;
1859 668 : return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
1860 : }
1861 :
1862 322 : void AccessorAssembler::TryProbeStubCacheTable(StubCache* stub_cache,
1863 : StubCacheTable table_id,
1864 : Node* entry_offset, Node* name,
1865 : Node* map, Label* if_handler,
1866 : Variable* var_handler,
1867 : Label* if_miss) {
1868 322 : StubCache::Table table = static_cast<StubCache::Table>(table_id);
1869 : #ifdef DEBUG
1870 : if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
1871 : Goto(if_miss);
1872 : return;
1873 : } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
1874 : Goto(if_miss);
1875 : return;
1876 : }
1877 : #endif
1878 : // The {table_offset} holds the entry offset times four (due to masking
1879 : // and shifting optimizations).
1880 : const int kMultiplier = sizeof(StubCache::Entry) >> Name::kHashShift;
1881 966 : entry_offset = IntPtrMul(entry_offset, IntPtrConstant(kMultiplier));
1882 :
1883 : // Check that the key in the entry matches the name.
1884 : Node* key_base =
1885 644 : ExternalConstant(ExternalReference(stub_cache->key_reference(table)));
1886 322 : Node* entry_key = Load(MachineType::Pointer(), key_base, entry_offset);
1887 644 : GotoIf(WordNotEqual(name, entry_key), if_miss);
1888 :
1889 : // Get the map entry from the cache.
1890 : DCHECK_EQ(kPointerSize * 2, stub_cache->map_reference(table).address() -
1891 : stub_cache->key_reference(table).address());
1892 : Node* entry_map =
1893 : Load(MachineType::Pointer(), key_base,
1894 966 : IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize * 2)));
1895 644 : GotoIf(WordNotEqual(map, entry_map), if_miss);
1896 :
1897 : DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() -
1898 : stub_cache->key_reference(table).address());
1899 : Node* handler = Load(MachineType::TaggedPointer(), key_base,
1900 966 : IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
1901 :
1902 : // We found the handler.
1903 322 : var_handler->Bind(handler);
1904 322 : Goto(if_handler);
1905 322 : }
1906 :
1907 161 : void AccessorAssembler::TryProbeStubCache(StubCache* stub_cache, Node* receiver,
1908 : Node* name, Label* if_handler,
1909 : Variable* var_handler,
1910 : Label* if_miss) {
1911 322 : Label try_secondary(this), miss(this);
1912 :
1913 161 : Counters* counters = isolate()->counters();
1914 161 : IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
1915 :
1916 : // Check that the {receiver} isn't a smi.
1917 322 : GotoIf(TaggedIsSmi(receiver), &miss);
1918 :
1919 322 : Node* receiver_map = LoadMap(receiver);
1920 :
1921 : // Probe the primary table.
1922 161 : Node* primary_offset = StubCachePrimaryOffset(name, receiver_map);
1923 : TryProbeStubCacheTable(stub_cache, kPrimary, primary_offset, name,
1924 161 : receiver_map, if_handler, var_handler, &try_secondary);
1925 :
1926 161 : BIND(&try_secondary);
1927 : {
1928 : // Probe the secondary table.
1929 161 : Node* secondary_offset = StubCacheSecondaryOffset(name, primary_offset);
1930 : TryProbeStubCacheTable(stub_cache, kSecondary, secondary_offset, name,
1931 161 : receiver_map, if_handler, var_handler, &miss);
1932 : }
1933 :
1934 161 : BIND(&miss);
1935 : {
1936 161 : IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
1937 161 : Goto(if_miss);
1938 161 : }
1939 161 : }
1940 :
1941 : //////////////////// Entry points into private implementation (one per stub).
1942 :
1943 93 : void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p,
1944 : ExitPoint* exit_point) {
1945 : // Must be kept in sync with LoadIC.
1946 :
1947 : // This function is hand-tuned to omit frame construction for common cases,
1948 : // e.g.: monomorphic field and constant loads through smi handlers.
1949 : // Polymorphic ICs with a hit in the first two entries also omit frames.
1950 : // TODO(jgruber): Frame omission is fragile and can be affected by minor
1951 : // changes in control flow and logic. We currently have no way of ensuring
1952 : // that no frame is constructed, so it's easy to break this optimization by
1953 : // accident.
1954 186 : Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred);
1955 :
1956 : // Inlined fast path.
1957 : {
1958 93 : Comment("LoadIC_BytecodeHandler_fast");
1959 :
1960 93 : Node* recv_map = LoadReceiverMap(p->receiver);
1961 186 : GotoIf(IsDeprecatedMap(recv_map), &miss);
1962 :
1963 93 : VARIABLE(var_handler, MachineRepresentation::kTagged);
1964 93 : Label try_polymorphic(this), if_handler(this, &var_handler);
1965 :
1966 : Node* feedback =
1967 : TryMonomorphicCase(p->slot, p->vector, recv_map, &if_handler,
1968 93 : &var_handler, &try_polymorphic);
1969 :
1970 93 : BIND(&if_handler);
1971 93 : HandleLoadICHandlerCase(p, var_handler.value(), &miss, exit_point);
1972 :
1973 93 : BIND(&try_polymorphic);
1974 : {
1975 279 : GotoIfNot(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
1976 93 : &stub_call);
1977 : HandlePolymorphicCase(recv_map, feedback, &if_handler, &var_handler,
1978 93 : &miss, 2);
1979 93 : }
1980 : }
1981 :
1982 93 : BIND(&stub_call);
1983 : {
1984 93 : Comment("LoadIC_BytecodeHandler_noninlined");
1985 :
1986 : // Call into the stub that implements the non-inlined parts of LoadIC.
1987 : Callable ic =
1988 93 : Builtins::CallableFor(isolate(), Builtins::kLoadIC_Noninlined);
1989 : Node* code_target = HeapConstant(ic.code());
1990 : exit_point->ReturnCallStub(ic.descriptor(), code_target, p->context,
1991 279 : p->receiver, p->name, p->slot, p->vector);
1992 : }
1993 :
1994 93 : BIND(&miss);
1995 : {
1996 93 : Comment("LoadIC_BytecodeHandler_miss");
1997 :
1998 : exit_point->ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context,
1999 93 : p->receiver, p->name, p->slot, p->vector);
2000 93 : }
2001 93 : }
2002 :
2003 31 : void AccessorAssembler::LoadIC(const LoadICParameters* p) {
2004 : // Must be kept in sync with LoadIC_BytecodeHandler.
2005 :
2006 31 : ExitPoint direct_exit(this);
2007 :
2008 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2009 31 : Label if_handler(this, &var_handler), non_inlined(this, Label::kDeferred),
2010 31 : try_polymorphic(this), miss(this, Label::kDeferred);
2011 :
2012 31 : Node* receiver_map = LoadReceiverMap(p->receiver);
2013 62 : GotoIf(IsDeprecatedMap(receiver_map), &miss);
2014 :
2015 : // Check monomorphic case.
2016 : Node* feedback =
2017 : TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2018 31 : &var_handler, &try_polymorphic);
2019 31 : BIND(&if_handler);
2020 31 : HandleLoadICHandlerCase(p, var_handler.value(), &miss, &direct_exit);
2021 :
2022 31 : BIND(&try_polymorphic);
2023 : {
2024 : // Check polymorphic case.
2025 31 : Comment("LoadIC_try_polymorphic");
2026 93 : GotoIfNot(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
2027 31 : &non_inlined);
2028 : HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
2029 31 : &miss, 2);
2030 : }
2031 :
2032 31 : BIND(&non_inlined);
2033 : LoadIC_Noninlined(p, receiver_map, feedback, &var_handler, &if_handler, &miss,
2034 31 : &direct_exit);
2035 :
2036 31 : BIND(&miss);
2037 : direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver,
2038 62 : p->name, p->slot, p->vector);
2039 31 : }
2040 :
2041 62 : void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p,
2042 : Node* receiver_map, Node* feedback,
2043 : Variable* var_handler,
2044 : Label* if_handler, Label* miss,
2045 : ExitPoint* exit_point) {
2046 62 : Label try_uninitialized(this, Label::kDeferred);
2047 :
2048 : // Neither deprecated map nor monomorphic. These cases are handled in the
2049 : // bytecode handler.
2050 : CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map)));
2051 : CSA_ASSERT(this,
2052 : WordNotEqual(receiver_map, LoadWeakCellValueUnchecked(feedback)));
2053 : CSA_ASSERT(this, WordNotEqual(LoadMap(feedback), FixedArrayMapConstant()));
2054 : DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
2055 :
2056 : {
2057 : // Check megamorphic case.
2058 62 : GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2059 62 : &try_uninitialized);
2060 :
2061 : TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name,
2062 62 : if_handler, var_handler, miss);
2063 : }
2064 :
2065 62 : BIND(&try_uninitialized);
2066 : {
2067 : // Check uninitialized case.
2068 : GotoIfNot(
2069 62 : WordEqual(feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2070 62 : miss);
2071 : exit_point->ReturnCallStub(
2072 : Builtins::CallableFor(isolate(), Builtins::kLoadIC_Uninitialized),
2073 124 : p->context, p->receiver, p->name, p->slot, p->vector);
2074 62 : }
2075 62 : }
2076 :
2077 31 : void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) {
2078 31 : Label miss(this, Label::kDeferred);
2079 31 : Node* receiver = p->receiver;
2080 62 : GotoIf(TaggedIsSmi(receiver), &miss);
2081 62 : Node* receiver_map = LoadMap(receiver);
2082 62 : Node* instance_type = LoadMapInstanceType(receiver_map);
2083 :
2084 : // Optimistically write the state transition to the vector.
2085 : StoreFeedbackVectorSlot(p->vector, p->slot,
2086 : LoadRoot(Heap::kpremonomorphic_symbolRootIndex),
2087 62 : SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS);
2088 :
2089 : {
2090 : // Special case for Function.prototype load, because it's very common
2091 : // for ICs that are only executed once (MyFunc.prototype.foo = ...).
2092 : Label not_function_prototype(this, Label::kDeferred);
2093 : GotoIfNot(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
2094 62 : ¬_function_prototype);
2095 93 : GotoIfNot(IsPrototypeString(p->name), ¬_function_prototype);
2096 :
2097 : // if (!(has_prototype_slot() && !has_non_instance_prototype())) use generic
2098 : // property loading mechanism.
2099 : int has_prototype_slot_mask = 1 << Map::kHasPrototypeSlot;
2100 : int has_non_instance_prototype_mask = 1 << Map::kHasNonInstancePrototype;
2101 : GotoIfNot(
2102 31 : Word32Equal(Word32And(LoadMapBitField(receiver_map),
2103 : Int32Constant(has_prototype_slot_mask |
2104 124 : has_non_instance_prototype_mask)),
2105 124 : Int32Constant(has_prototype_slot_mask)),
2106 62 : ¬_function_prototype);
2107 62 : Return(LoadJSFunctionPrototype(receiver, &miss));
2108 31 : BIND(¬_function_prototype);
2109 : }
2110 :
2111 : GenericPropertyLoad(receiver, receiver_map, instance_type, p, &miss,
2112 31 : kDontUseStubCache);
2113 :
2114 31 : BIND(&miss);
2115 : {
2116 : // Undo the optimistic state transition.
2117 : StoreFeedbackVectorSlot(p->vector, p->slot,
2118 : LoadRoot(Heap::kuninitialized_symbolRootIndex),
2119 62 : SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS);
2120 :
2121 : TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
2122 31 : p->slot, p->vector);
2123 31 : }
2124 31 : }
2125 :
2126 62 : void AccessorAssembler::LoadICProtoArray(
2127 : const LoadICParameters* p, Node* handler,
2128 : bool throw_reference_error_if_nonexistent) {
2129 62 : Label miss(this);
2130 : CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
2131 : CSA_ASSERT(this, IsFixedArrayMap(LoadMap(handler)));
2132 :
2133 62 : ExitPoint direct_exit(this);
2134 :
2135 : Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
2136 124 : Node* handler_flags = SmiUntag(smi_handler);
2137 :
2138 124 : Node* handler_length = LoadAndUntagFixedArrayBaseLength(handler);
2139 :
2140 : Node* holder = EmitLoadICProtoArrayCheck(p, handler, handler_length,
2141 62 : handler_flags, &miss);
2142 :
2143 : HandleLoadICSmiHandlerCase(p, holder, smi_handler, &miss, &direct_exit,
2144 : throw_reference_error_if_nonexistent,
2145 62 : kOnlyProperties);
2146 :
2147 62 : BIND(&miss);
2148 : {
2149 : TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
2150 62 : p->slot, p->vector);
2151 62 : }
2152 62 : }
2153 :
2154 434 : void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase(
2155 : Node* vector, Node* slot, ExitPoint* exit_point, Label* try_handler,
2156 : Label* miss, ParameterMode slot_mode) {
2157 434 : Comment("LoadGlobalIC_TryPropertyCellCase");
2158 :
2159 434 : Node* weak_cell = LoadFeedbackVectorSlot(vector, slot, 0, slot_mode);
2160 : CSA_ASSERT(this, HasInstanceType(weak_cell, WEAK_CELL_TYPE));
2161 :
2162 : // Load value or try handler case if the {weak_cell} is cleared.
2163 434 : Node* property_cell = LoadWeakCellValue(weak_cell, try_handler);
2164 : CSA_ASSERT(this, IsPropertyCell(property_cell));
2165 :
2166 : Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset);
2167 868 : GotoIf(WordEqual(value, TheHoleConstant()), miss);
2168 434 : exit_point->Return(value);
2169 434 : }
2170 :
2171 434 : void AccessorAssembler::LoadGlobalIC_TryHandlerCase(const LoadICParameters* pp,
2172 : TypeofMode typeof_mode,
2173 : ExitPoint* exit_point,
2174 : Label* miss) {
2175 434 : Comment("LoadGlobalIC_TryHandlerCase");
2176 :
2177 434 : Label call_handler(this), non_smi(this);
2178 :
2179 : Node* handler = LoadFeedbackVectorSlot(pp->vector, pp->slot, kPointerSize,
2180 434 : SMI_PARAMETERS);
2181 434 : GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2182 434 : miss);
2183 :
2184 868 : GotoIfNot(TaggedIsSmi(handler), &non_smi);
2185 :
2186 434 : bool throw_reference_error_if_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF;
2187 :
2188 : {
2189 434 : LoadICParameters p = *pp;
2190 : DCHECK_NULL(p.receiver);
2191 1302 : Node* native_context = LoadNativeContext(p.context);
2192 868 : p.receiver =
2193 868 : LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX);
2194 868 : Node* holder = LoadContextElement(native_context, Context::EXTENSION_INDEX);
2195 : HandleLoadICSmiHandlerCase(&p, holder, handler, miss, exit_point,
2196 : throw_reference_error_if_nonexistent,
2197 434 : kOnlyProperties);
2198 : }
2199 :
2200 434 : BIND(&non_smi);
2201 1302 : GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
2202 :
2203 : HandleLoadGlobalICHandlerCase(pp, handler, miss, exit_point,
2204 434 : throw_reference_error_if_nonexistent);
2205 :
2206 434 : BIND(&call_handler);
2207 : {
2208 434 : LoadWithVectorDescriptor descriptor(isolate());
2209 1302 : Node* native_context = LoadNativeContext(pp->context);
2210 : Node* receiver =
2211 868 : LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX);
2212 : exit_point->ReturnCallStub(descriptor, handler, pp->context, receiver,
2213 434 : pp->name, pp->slot, pp->vector);
2214 434 : }
2215 434 : }
2216 :
2217 434 : void AccessorAssembler::LoadGlobalIC_MissCase(const LoadICParameters* p,
2218 : ExitPoint* exit_point) {
2219 434 : Comment("LoadGlobalIC_MissCase");
2220 :
2221 : exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context,
2222 434 : p->name, p->slot, p->vector);
2223 434 : }
2224 :
2225 62 : void AccessorAssembler::LoadGlobalIC(const LoadICParameters* p,
2226 : TypeofMode typeof_mode) {
2227 : // Must be kept in sync with Interpreter::BuildLoadGlobal.
2228 :
2229 62 : ExitPoint direct_exit(this);
2230 :
2231 124 : Label try_handler(this), miss(this);
2232 : LoadGlobalIC_TryPropertyCellCase(p->vector, p->slot, &direct_exit,
2233 62 : &try_handler, &miss);
2234 :
2235 62 : BIND(&try_handler);
2236 62 : LoadGlobalIC_TryHandlerCase(p, typeof_mode, &direct_exit, &miss);
2237 :
2238 62 : BIND(&miss);
2239 124 : LoadGlobalIC_MissCase(p, &direct_exit);
2240 62 : }
2241 :
2242 31 : void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) {
2243 31 : ExitPoint direct_exit(this);
2244 :
2245 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2246 31 : Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred),
2247 31 : try_megamorphic(this, Label::kDeferred),
2248 31 : try_polymorphic_name(this, Label::kDeferred),
2249 31 : miss(this, Label::kDeferred);
2250 :
2251 31 : Node* receiver_map = LoadReceiverMap(p->receiver);
2252 62 : GotoIf(IsDeprecatedMap(receiver_map), &miss);
2253 :
2254 : // Check monomorphic case.
2255 : Node* feedback =
2256 : TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2257 31 : &var_handler, &try_polymorphic);
2258 31 : BIND(&if_handler);
2259 : {
2260 : HandleLoadICHandlerCase(p, var_handler.value(), &miss, &direct_exit,
2261 31 : kSupportElements);
2262 : }
2263 :
2264 31 : BIND(&try_polymorphic);
2265 : {
2266 : // Check polymorphic case.
2267 31 : Comment("KeyedLoadIC_try_polymorphic");
2268 93 : GotoIfNot(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
2269 31 : &try_megamorphic);
2270 : HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
2271 31 : &miss, 2);
2272 : }
2273 :
2274 31 : BIND(&try_megamorphic);
2275 : {
2276 : // Check megamorphic case.
2277 31 : Comment("KeyedLoadIC_try_megamorphic");
2278 31 : GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2279 31 : &try_polymorphic_name);
2280 : // TODO(jkummerow): Inline this? Or some of it?
2281 : TailCallStub(
2282 : Builtins::CallableFor(isolate(), Builtins::kKeyedLoadIC_Megamorphic),
2283 62 : p->context, p->receiver, p->name, p->slot, p->vector);
2284 : }
2285 31 : BIND(&try_polymorphic_name);
2286 : {
2287 : // We might have a name in feedback, and a fixed array in the next slot.
2288 31 : Node* name = p->name;
2289 31 : Comment("KeyedLoadIC_try_polymorphic_name");
2290 31 : VARIABLE(var_name, MachineRepresentation::kTagged, name);
2291 62 : VARIABLE(var_index, MachineType::PointerRepresentation());
2292 31 : Label if_polymorphic_name(this, &var_name), if_internalized(this),
2293 31 : if_notinternalized(this, Label::kDeferred);
2294 :
2295 : // Fast-case: The recorded {feedback} matches the {name}.
2296 62 : GotoIf(WordEqual(feedback, name), &if_polymorphic_name);
2297 :
2298 : // Try to internalize the {name} if it isn't already.
2299 : TryToName(name, &miss, &var_index, &if_internalized, &var_name, &miss,
2300 31 : &if_notinternalized);
2301 :
2302 31 : BIND(&if_internalized);
2303 : {
2304 : // The {var_name} now contains a unique name.
2305 62 : Branch(WordEqual(feedback, var_name.value()), &if_polymorphic_name,
2306 62 : &miss);
2307 : }
2308 :
2309 31 : BIND(&if_notinternalized);
2310 : {
2311 : // Try to internalize the {name}.
2312 : Node* function = ExternalConstant(
2313 62 : ExternalReference::try_internalize_string_function(isolate()));
2314 : var_name.Bind(CallCFunction1(MachineType::AnyTagged(),
2315 31 : MachineType::AnyTagged(), function, name));
2316 31 : Goto(&if_internalized);
2317 : }
2318 :
2319 31 : BIND(&if_polymorphic_name);
2320 : {
2321 : // If the name comparison succeeded, we know we have a fixed array with
2322 : // at least one map/handler pair.
2323 31 : Node* name = var_name.value();
2324 : TailCallBuiltin(Builtins::kKeyedLoadIC_PolymorphicName, p->context,
2325 31 : p->receiver, name, p->slot, p->vector);
2326 31 : }
2327 : }
2328 :
2329 31 : BIND(&miss);
2330 : {
2331 31 : Comment("KeyedLoadIC_miss");
2332 : TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
2333 31 : p->name, p->slot, p->vector);
2334 31 : }
2335 31 : }
2336 :
2337 31 : void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
2338 31 : VARIABLE(var_index, MachineType::PointerRepresentation());
2339 62 : VARIABLE(var_unique, MachineRepresentation::kTagged);
2340 31 : var_unique.Bind(p->name); // Dummy initialization.
2341 31 : Label if_index(this), if_unique_name(this), if_notunique(this), slow(this);
2342 :
2343 31 : Node* receiver = p->receiver;
2344 62 : GotoIf(TaggedIsSmi(receiver), &slow);
2345 62 : Node* receiver_map = LoadMap(receiver);
2346 62 : Node* instance_type = LoadMapInstanceType(receiver_map);
2347 :
2348 : TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
2349 31 : &if_notunique);
2350 :
2351 31 : BIND(&if_index);
2352 : {
2353 : GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(),
2354 31 : &slow);
2355 : }
2356 :
2357 31 : BIND(&if_unique_name);
2358 : {
2359 31 : LoadICParameters pp = *p;
2360 31 : pp.name = var_unique.value();
2361 31 : GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow);
2362 : }
2363 :
2364 31 : BIND(&if_notunique);
2365 : {
2366 : if (FLAG_internalize_on_the_fly) {
2367 : Label not_in_string_table(this);
2368 : TryInternalizeString(p->name, &if_index, &var_index, &if_unique_name,
2369 : &var_unique, ¬_in_string_table, &slow);
2370 :
2371 : BIND(¬_in_string_table);
2372 : // If the string was not found in the string table, then no object can
2373 : // have a property with that name.
2374 : Return(UndefinedConstant());
2375 : } else {
2376 31 : Goto(&slow);
2377 : }
2378 : }
2379 :
2380 31 : BIND(&slow);
2381 : {
2382 31 : Comment("KeyedLoadGeneric_slow");
2383 62 : IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1);
2384 : // TODO(jkummerow): Should we use the GetProperty TF stub instead?
2385 : TailCallRuntime(Runtime::kKeyedGetProperty, p->context, p->receiver,
2386 31 : p->name);
2387 31 : }
2388 31 : }
2389 :
2390 31 : void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) {
2391 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2392 31 : Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
2393 :
2394 31 : Node* receiver = p->receiver;
2395 31 : Node* receiver_map = LoadReceiverMap(receiver);
2396 31 : Node* name = p->name;
2397 31 : Node* vector = p->vector;
2398 31 : Node* slot = p->slot;
2399 31 : Node* context = p->context;
2400 :
2401 : // When we get here, we know that the {name} matches the recorded
2402 : // feedback name in the {vector} and can safely be used for the
2403 : // LoadIC handler logic below.
2404 : CSA_ASSERT(this, IsName(name));
2405 : CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map)));
2406 : CSA_ASSERT(this, WordEqual(name, LoadFeedbackVectorSlot(vector, slot, 0,
2407 : SMI_PARAMETERS)));
2408 :
2409 : // Check if we have a matching handler for the {receiver_map}.
2410 : Node* array =
2411 31 : LoadFeedbackVectorSlot(vector, slot, kPointerSize, SMI_PARAMETERS);
2412 : HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss,
2413 31 : 1);
2414 :
2415 31 : BIND(&if_handler);
2416 : {
2417 : ExitPoint direct_exit(this);
2418 : HandleLoadICHandlerCase(p, var_handler.value(), &miss, &direct_exit,
2419 31 : kOnlyProperties);
2420 : }
2421 :
2422 31 : BIND(&miss);
2423 : {
2424 31 : Comment("KeyedLoadIC_miss");
2425 : TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, name, slot,
2426 : vector);
2427 31 : }
2428 31 : }
2429 :
2430 31 : void AccessorAssembler::StoreIC(const StoreICParameters* p) {
2431 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2432 31 : Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred),
2433 31 : try_megamorphic(this, Label::kDeferred),
2434 31 : try_uninitialized(this, Label::kDeferred), miss(this, Label::kDeferred);
2435 :
2436 31 : Node* receiver_map = LoadReceiverMap(p->receiver);
2437 62 : GotoIf(IsDeprecatedMap(receiver_map), &miss);
2438 :
2439 : // Check monomorphic case.
2440 : Node* feedback =
2441 : TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2442 31 : &var_handler, &try_polymorphic);
2443 31 : BIND(&if_handler);
2444 : {
2445 31 : Comment("StoreIC_if_handler");
2446 31 : HandleStoreICHandlerCase(p, var_handler.value(), &miss);
2447 : }
2448 :
2449 31 : BIND(&try_polymorphic);
2450 : {
2451 : // Check polymorphic case.
2452 31 : Comment("StoreIC_try_polymorphic");
2453 : GotoIfNot(
2454 93 : WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
2455 31 : &try_megamorphic);
2456 : HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
2457 31 : &miss, 2);
2458 : }
2459 :
2460 31 : BIND(&try_megamorphic);
2461 : {
2462 : // Check megamorphic case.
2463 31 : GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2464 31 : &try_uninitialized);
2465 :
2466 : TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name,
2467 31 : &if_handler, &var_handler, &miss);
2468 : }
2469 31 : BIND(&try_uninitialized);
2470 : {
2471 : // Check uninitialized case.
2472 : GotoIfNot(
2473 31 : WordEqual(feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2474 31 : &miss);
2475 : Callable stub =
2476 31 : Builtins::CallableFor(isolate(), Builtins::kStoreIC_Uninitialized);
2477 : TailCallStub(stub, p->context, p->receiver, p->name, p->value, p->slot,
2478 31 : p->vector);
2479 : }
2480 31 : BIND(&miss);
2481 : {
2482 : TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot,
2483 31 : p->vector, p->receiver, p->name);
2484 31 : }
2485 31 : }
2486 :
2487 310 : void AccessorAssembler::StoreGlobalIC_PropertyCellCase(Node* property_cell,
2488 : Node* value,
2489 : ExitPoint* exit_point,
2490 : Label* miss) {
2491 310 : Comment("StoreGlobalIC_TryPropertyCellCase");
2492 : CSA_ASSERT(this, IsPropertyCell(property_cell));
2493 :
2494 : // Load the payload of the global parameter cell. A hole indicates that
2495 : // the cell has been invalidated and that the store must be handled by the
2496 : // runtime.
2497 : Node* cell_contents =
2498 310 : LoadObjectField(property_cell, PropertyCell::kValueOffset);
2499 : Node* details = LoadAndUntagToWord32ObjectField(property_cell,
2500 620 : PropertyCell::kDetailsOffset);
2501 : Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details);
2502 :
2503 310 : Label constant(this), store(this), not_smi(this);
2504 :
2505 : GotoIf(Word32Equal(type, Int32Constant(
2506 620 : static_cast<int>(PropertyCellType::kConstant))),
2507 620 : &constant);
2508 :
2509 620 : GotoIf(IsTheHole(cell_contents), miss);
2510 :
2511 : GotoIf(Word32Equal(
2512 620 : type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))),
2513 620 : &store);
2514 : CSA_ASSERT(this,
2515 : Word32Or(Word32Equal(type, Int32Constant(static_cast<int>(
2516 : PropertyCellType::kConstantType))),
2517 : Word32Equal(type, Int32Constant(static_cast<int>(
2518 : PropertyCellType::kUndefined)))));
2519 :
2520 620 : GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi);
2521 620 : GotoIfNot(TaggedIsSmi(value), miss);
2522 310 : Goto(&store);
2523 :
2524 310 : BIND(¬_smi);
2525 : {
2526 620 : GotoIf(TaggedIsSmi(value), miss);
2527 620 : Node* expected_map = LoadMap(cell_contents);
2528 620 : Node* map = LoadMap(value);
2529 620 : GotoIfNot(WordEqual(expected_map, map), miss);
2530 310 : Goto(&store);
2531 : }
2532 :
2533 310 : BIND(&store);
2534 : {
2535 310 : StoreObjectField(property_cell, PropertyCell::kValueOffset, value);
2536 310 : exit_point->Return(value);
2537 : }
2538 :
2539 310 : BIND(&constant);
2540 : {
2541 620 : GotoIfNot(WordEqual(cell_contents, value), miss);
2542 310 : exit_point->Return(value);
2543 310 : }
2544 310 : }
2545 :
2546 31 : void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) {
2547 31 : Label miss(this, Label::kDeferred);
2548 : {
2549 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2550 :
2551 31 : Label if_handler(this, &var_handler),
2552 31 : try_polymorphic(this, Label::kDeferred),
2553 31 : try_megamorphic(this, Label::kDeferred),
2554 31 : try_polymorphic_name(this, Label::kDeferred);
2555 :
2556 31 : Node* receiver_map = LoadReceiverMap(p->receiver);
2557 62 : GotoIf(IsDeprecatedMap(receiver_map), &miss);
2558 :
2559 : // Check monomorphic case.
2560 : Node* feedback =
2561 : TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2562 31 : &var_handler, &try_polymorphic);
2563 31 : BIND(&if_handler);
2564 : {
2565 31 : Comment("KeyedStoreIC_if_handler");
2566 31 : HandleStoreICHandlerCase(p, var_handler.value(), &miss, kSupportElements);
2567 : }
2568 :
2569 31 : BIND(&try_polymorphic);
2570 : {
2571 : // CheckPolymorphic case.
2572 31 : Comment("KeyedStoreIC_try_polymorphic");
2573 : GotoIfNot(
2574 93 : WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
2575 31 : &try_megamorphic);
2576 : HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
2577 31 : &miss, 2);
2578 : }
2579 :
2580 31 : BIND(&try_megamorphic);
2581 : {
2582 : // Check megamorphic case.
2583 31 : Comment("KeyedStoreIC_try_megamorphic");
2584 : GotoIfNot(
2585 31 : WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2586 31 : &try_polymorphic_name);
2587 : TailCallStub(
2588 : Builtins::CallableFor(isolate(), Builtins::kKeyedStoreIC_Megamorphic),
2589 62 : p->context, p->receiver, p->name, p->value, p->slot, p->vector);
2590 : }
2591 :
2592 31 : BIND(&try_polymorphic_name);
2593 : {
2594 : // We might have a name in feedback, and a fixed array in the next slot.
2595 31 : Comment("KeyedStoreIC_try_polymorphic_name");
2596 93 : GotoIfNot(WordEqual(feedback, p->name), &miss);
2597 : // If the name comparison succeeded, we know we have a feedback vector
2598 : // with at least one map/handler pair.
2599 : Node* array = LoadFeedbackVectorSlot(p->vector, p->slot, kPointerSize,
2600 31 : SMI_PARAMETERS);
2601 : HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler,
2602 31 : &miss, 1);
2603 31 : }
2604 : }
2605 31 : BIND(&miss);
2606 : {
2607 31 : Comment("KeyedStoreIC_miss");
2608 : TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
2609 31 : p->vector, p->receiver, p->name);
2610 31 : }
2611 31 : }
2612 :
2613 : //////////////////// Public methods.
2614 :
2615 31 : void AccessorAssembler::GenerateLoadIC() {
2616 : typedef LoadWithVectorDescriptor Descriptor;
2617 :
2618 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2619 31 : Node* name = Parameter(Descriptor::kName);
2620 31 : Node* slot = Parameter(Descriptor::kSlot);
2621 31 : Node* vector = Parameter(Descriptor::kVector);
2622 31 : Node* context = Parameter(Descriptor::kContext);
2623 :
2624 : LoadICParameters p(context, receiver, name, slot, vector);
2625 31 : LoadIC(&p);
2626 31 : }
2627 :
2628 31 : void AccessorAssembler::GenerateLoadIC_Noninlined() {
2629 : typedef LoadWithVectorDescriptor Descriptor;
2630 :
2631 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2632 31 : Node* name = Parameter(Descriptor::kName);
2633 31 : Node* slot = Parameter(Descriptor::kSlot);
2634 31 : Node* vector = Parameter(Descriptor::kVector);
2635 31 : Node* context = Parameter(Descriptor::kContext);
2636 :
2637 31 : ExitPoint direct_exit(this);
2638 31 : VARIABLE(var_handler, MachineRepresentation::kTagged);
2639 31 : Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
2640 :
2641 31 : Node* receiver_map = LoadReceiverMap(receiver);
2642 31 : Node* feedback = LoadFeedbackVectorSlot(vector, slot, 0, SMI_PARAMETERS);
2643 :
2644 : LoadICParameters p(context, receiver, name, slot, vector);
2645 : LoadIC_Noninlined(&p, receiver_map, feedback, &var_handler, &if_handler,
2646 31 : &miss, &direct_exit);
2647 :
2648 31 : BIND(&if_handler);
2649 31 : HandleLoadICHandlerCase(&p, var_handler.value(), &miss, &direct_exit);
2650 :
2651 31 : BIND(&miss);
2652 : direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name,
2653 62 : slot, vector);
2654 31 : }
2655 :
2656 31 : void AccessorAssembler::GenerateLoadIC_Uninitialized() {
2657 : typedef LoadWithVectorDescriptor Descriptor;
2658 :
2659 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2660 31 : Node* name = Parameter(Descriptor::kName);
2661 31 : Node* slot = Parameter(Descriptor::kSlot);
2662 31 : Node* vector = Parameter(Descriptor::kVector);
2663 31 : Node* context = Parameter(Descriptor::kContext);
2664 :
2665 : LoadICParameters p(context, receiver, name, slot, vector);
2666 31 : LoadIC_Uninitialized(&p);
2667 31 : }
2668 :
2669 31 : void AccessorAssembler::GenerateLoadICTrampoline() {
2670 : typedef LoadDescriptor Descriptor;
2671 :
2672 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2673 31 : Node* name = Parameter(Descriptor::kName);
2674 31 : Node* slot = Parameter(Descriptor::kSlot);
2675 31 : Node* context = Parameter(Descriptor::kContext);
2676 31 : Node* vector = LoadFeedbackVectorForStub();
2677 :
2678 31 : TailCallBuiltin(Builtins::kLoadIC, context, receiver, name, slot, vector);
2679 31 : }
2680 :
2681 62 : void AccessorAssembler::GenerateLoadICProtoArray(
2682 : bool throw_reference_error_if_nonexistent) {
2683 : typedef LoadICProtoArrayDescriptor Descriptor;
2684 :
2685 62 : Node* receiver = Parameter(Descriptor::kReceiver);
2686 62 : Node* name = Parameter(Descriptor::kName);
2687 62 : Node* slot = Parameter(Descriptor::kSlot);
2688 62 : Node* vector = Parameter(Descriptor::kVector);
2689 62 : Node* handler = Parameter(Descriptor::kHandler);
2690 62 : Node* context = Parameter(Descriptor::kContext);
2691 :
2692 : LoadICParameters p(context, receiver, name, slot, vector);
2693 62 : LoadICProtoArray(&p, handler, throw_reference_error_if_nonexistent);
2694 62 : }
2695 :
2696 31 : void AccessorAssembler::GenerateLoadField() {
2697 : typedef LoadFieldDescriptor Descriptor;
2698 :
2699 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2700 : Node* name = nullptr;
2701 : Node* slot = nullptr;
2702 : Node* vector = nullptr;
2703 31 : Node* context = Parameter(Descriptor::kContext);
2704 : LoadICParameters p(context, receiver, name, slot, vector);
2705 :
2706 31 : ExitPoint direct_exit(this);
2707 :
2708 31 : VARIABLE(var_double_value, MachineRepresentation::kFloat64);
2709 31 : Label rebox_double(this, &var_double_value);
2710 :
2711 31 : Node* smi_handler = Parameter(Descriptor::kSmiHandler);
2712 62 : Node* handler_word = SmiUntag(smi_handler);
2713 : HandleLoadField(receiver, handler_word, &var_double_value, &rebox_double,
2714 31 : &direct_exit);
2715 :
2716 31 : BIND(&rebox_double);
2717 124 : Return(AllocateHeapNumberWithValue(var_double_value.value()));
2718 31 : }
2719 :
2720 62 : void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) {
2721 : typedef LoadGlobalWithVectorDescriptor Descriptor;
2722 :
2723 62 : Node* name = Parameter(Descriptor::kName);
2724 62 : Node* slot = Parameter(Descriptor::kSlot);
2725 62 : Node* vector = Parameter(Descriptor::kVector);
2726 62 : Node* context = Parameter(Descriptor::kContext);
2727 :
2728 : LoadICParameters p(context, nullptr, name, slot, vector);
2729 62 : LoadGlobalIC(&p, typeof_mode);
2730 62 : }
2731 :
2732 62 : void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) {
2733 : typedef LoadGlobalDescriptor Descriptor;
2734 :
2735 62 : Node* name = Parameter(Descriptor::kName);
2736 62 : Node* slot = Parameter(Descriptor::kSlot);
2737 62 : Node* context = Parameter(Descriptor::kContext);
2738 62 : Node* vector = LoadFeedbackVectorForStub();
2739 :
2740 : Callable callable =
2741 62 : CodeFactory::LoadGlobalICInOptimizedCode(isolate(), typeof_mode);
2742 62 : TailCallStub(callable, context, name, slot, vector);
2743 62 : }
2744 :
2745 31 : void AccessorAssembler::GenerateKeyedLoadIC() {
2746 : typedef LoadWithVectorDescriptor Descriptor;
2747 :
2748 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2749 31 : Node* name = Parameter(Descriptor::kName);
2750 31 : Node* slot = Parameter(Descriptor::kSlot);
2751 31 : Node* vector = Parameter(Descriptor::kVector);
2752 31 : Node* context = Parameter(Descriptor::kContext);
2753 :
2754 : LoadICParameters p(context, receiver, name, slot, vector);
2755 31 : KeyedLoadIC(&p);
2756 31 : }
2757 :
2758 31 : void AccessorAssembler::GenerateKeyedLoadICTrampoline() {
2759 : typedef LoadDescriptor Descriptor;
2760 :
2761 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2762 31 : Node* name = Parameter(Descriptor::kName);
2763 31 : Node* slot = Parameter(Descriptor::kSlot);
2764 31 : Node* context = Parameter(Descriptor::kContext);
2765 31 : Node* vector = LoadFeedbackVectorForStub();
2766 :
2767 : TailCallBuiltin(Builtins::kKeyedLoadIC, context, receiver, name, slot,
2768 31 : vector);
2769 31 : }
2770 :
2771 31 : void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() {
2772 : typedef LoadWithVectorDescriptor Descriptor;
2773 :
2774 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2775 31 : Node* name = Parameter(Descriptor::kName);
2776 31 : Node* slot = Parameter(Descriptor::kSlot);
2777 31 : Node* vector = Parameter(Descriptor::kVector);
2778 31 : Node* context = Parameter(Descriptor::kContext);
2779 :
2780 : LoadICParameters p(context, receiver, name, slot, vector);
2781 31 : KeyedLoadICGeneric(&p);
2782 31 : }
2783 :
2784 31 : void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() {
2785 : typedef LoadWithVectorDescriptor Descriptor;
2786 :
2787 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2788 31 : Node* name = Parameter(Descriptor::kName);
2789 31 : Node* slot = Parameter(Descriptor::kSlot);
2790 31 : Node* vector = Parameter(Descriptor::kVector);
2791 31 : Node* context = Parameter(Descriptor::kContext);
2792 :
2793 : LoadICParameters p(context, receiver, name, slot, vector);
2794 31 : KeyedLoadICPolymorphicName(&p);
2795 31 : }
2796 :
2797 31 : void AccessorAssembler::GenerateStoreIC() {
2798 : typedef StoreWithVectorDescriptor Descriptor;
2799 :
2800 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2801 31 : Node* name = Parameter(Descriptor::kName);
2802 31 : Node* value = Parameter(Descriptor::kValue);
2803 31 : Node* slot = Parameter(Descriptor::kSlot);
2804 31 : Node* vector = Parameter(Descriptor::kVector);
2805 31 : Node* context = Parameter(Descriptor::kContext);
2806 :
2807 : StoreICParameters p(context, receiver, name, value, slot, vector);
2808 31 : StoreIC(&p);
2809 31 : }
2810 :
2811 31 : void AccessorAssembler::GenerateStoreICTrampoline() {
2812 : typedef StoreDescriptor Descriptor;
2813 :
2814 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2815 31 : Node* name = Parameter(Descriptor::kName);
2816 31 : Node* value = Parameter(Descriptor::kValue);
2817 31 : Node* slot = Parameter(Descriptor::kSlot);
2818 31 : Node* context = Parameter(Descriptor::kContext);
2819 31 : Node* vector = LoadFeedbackVectorForStub();
2820 :
2821 31 : Callable callable = Builtins::CallableFor(isolate(), Builtins::kStoreIC);
2822 31 : TailCallStub(callable, context, receiver, name, value, slot, vector);
2823 31 : }
2824 :
2825 31 : void AccessorAssembler::GenerateKeyedStoreIC() {
2826 : typedef StoreWithVectorDescriptor Descriptor;
2827 :
2828 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2829 31 : Node* name = Parameter(Descriptor::kName);
2830 31 : Node* value = Parameter(Descriptor::kValue);
2831 31 : Node* slot = Parameter(Descriptor::kSlot);
2832 31 : Node* vector = Parameter(Descriptor::kVector);
2833 31 : Node* context = Parameter(Descriptor::kContext);
2834 :
2835 : StoreICParameters p(context, receiver, name, value, slot, vector);
2836 31 : KeyedStoreIC(&p);
2837 31 : }
2838 :
2839 31 : void AccessorAssembler::GenerateKeyedStoreICTrampoline() {
2840 : typedef StoreDescriptor Descriptor;
2841 :
2842 31 : Node* receiver = Parameter(Descriptor::kReceiver);
2843 31 : Node* name = Parameter(Descriptor::kName);
2844 31 : Node* value = Parameter(Descriptor::kValue);
2845 31 : Node* slot = Parameter(Descriptor::kSlot);
2846 31 : Node* context = Parameter(Descriptor::kContext);
2847 31 : Node* vector = LoadFeedbackVectorForStub();
2848 :
2849 31 : Callable callable = Builtins::CallableFor(isolate(), Builtins::kKeyedStoreIC);
2850 31 : TailCallStub(callable, context, receiver, name, value, slot, vector);
2851 31 : }
2852 :
2853 : } // namespace internal
2854 : } // namespace v8
|