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