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 #include "src/objects/module.h"
15
16 namespace v8 {
17 namespace internal {
18
19 using compiler::CodeAssemblerState;
20 using compiler::Node;
21 template <typename T>
22 using TNode = compiler::TNode<T>;
23 template <typename T>
24 using SloppyTNode = compiler::SloppyTNode<T>;
25
26 //////////////////// Private helpers.
27
28 // Loads dataX field from the DataHandler object.
LoadHandlerDataField(SloppyTNode<DataHandler> handler,int data_index)29 TNode<MaybeObject> AccessorAssembler::LoadHandlerDataField(
30 SloppyTNode<DataHandler> handler, int data_index) {
31 #ifdef DEBUG
32 TNode<Map> handler_map = LoadMap(handler);
33 TNode<Int32T> instance_type = LoadMapInstanceType(handler_map);
34 #endif
35 CSA_ASSERT(this,
36 Word32Or(InstanceTypeEqual(instance_type, LOAD_HANDLER_TYPE),
37 InstanceTypeEqual(instance_type, STORE_HANDLER_TYPE)));
38 int offset = 0;
39 int minimum_size = 0;
40 switch (data_index) {
41 case 1:
42 offset = DataHandler::kData1Offset;
43 minimum_size = DataHandler::kSizeWithData1;
44 break;
45 case 2:
46 offset = DataHandler::kData2Offset;
47 minimum_size = DataHandler::kSizeWithData2;
48 break;
49 case 3:
50 offset = DataHandler::kData3Offset;
51 minimum_size = DataHandler::kSizeWithData3;
52 break;
53 default:
54 UNREACHABLE();
55 break;
56 }
57 USE(minimum_size);
58 CSA_ASSERT(this, UintPtrGreaterThanOrEqual(
59 LoadMapInstanceSizeInWords(handler_map),
60 IntPtrConstant(minimum_size / kPointerSize)));
61 return LoadMaybeWeakObjectField(handler, offset);
62 }
63
TryMonomorphicCase(Node * slot,Node * vector,Node * receiver_map,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)64 TNode<MaybeObject> AccessorAssembler::TryMonomorphicCase(
65 Node* slot, Node* vector, Node* receiver_map, Label* if_handler,
66 TVariable<MaybeObject>* var_handler, Label* if_miss) {
67 Comment("TryMonomorphicCase");
68 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
69
70 // TODO(ishell): add helper class that hides offset computations for a series
71 // of loads.
72 CSA_ASSERT(this, IsFeedbackVector(vector), vector);
73 int32_t header_size = FeedbackVector::kFeedbackSlotsOffset - kHeapObjectTag;
74 // Adding |header_size| with a separate IntPtrAdd rather than passing it
75 // into ElementOffsetFromIndex() allows it to be folded into a single
76 // [base, index, offset] indirect memory access on x64.
77 Node* offset = ElementOffsetFromIndex(slot, HOLEY_ELEMENTS, SMI_PARAMETERS);
78 TNode<MaybeObject> feedback = ReinterpretCast<MaybeObject>(
79 Load(MachineType::AnyTagged(), vector,
80 IntPtrAdd(offset, IntPtrConstant(header_size))));
81
82 // Try to quickly handle the monomorphic case without knowing for sure
83 // if we have a weak reference in feedback.
84 GotoIf(IsNotWeakReferenceTo(feedback, CAST(receiver_map)), if_miss);
85
86 TNode<MaybeObject> handler = UncheckedCast<MaybeObject>(
87 Load(MachineType::AnyTagged(), vector,
88 IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize))));
89
90 *var_handler = handler;
91 Goto(if_handler);
92 return feedback;
93 }
94
HandlePolymorphicCase(Node * receiver_map,TNode<WeakFixedArray> feedback,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss,int min_feedback_capacity)95 void AccessorAssembler::HandlePolymorphicCase(
96 Node* receiver_map, TNode<WeakFixedArray> feedback, Label* if_handler,
97 TVariable<MaybeObject>* var_handler, Label* if_miss,
98 int min_feedback_capacity) {
99 Comment("HandlePolymorphicCase");
100 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
101
102 // Deferred so the unrolled case can omit frame construction in bytecode
103 // handler.
104 Label loop(this, Label::kDeferred);
105
106 // Iterate {feedback} array.
107 const int kEntrySize = 2;
108
109 // Loading feedback's length is delayed until we need it when looking past
110 // the first {min_feedback_capacity} (map, handler) pairs.
111 Node* length = nullptr;
112 CSA_ASSERT(this, SmiGreaterThanOrEqual(
113 LoadWeakFixedArrayLength(feedback),
114 SmiConstant(min_feedback_capacity * kEntrySize)));
115
116 const int kUnrolledIterations = IC::kMaxPolymorphicMapCount;
117 for (int i = 0; i < kUnrolledIterations; i++) {
118 int map_index = i * kEntrySize;
119 int handler_index = i * kEntrySize + 1;
120
121 if (i >= min_feedback_capacity) {
122 if (length == nullptr) length = LoadWeakFixedArrayLength(feedback);
123 GotoIf(SmiGreaterThanOrEqual(SmiConstant(handler_index), CAST(length)),
124 if_miss);
125 }
126
127 Label next_entry(this);
128 TNode<MaybeObject> maybe_cached_map =
129 LoadWeakFixedArrayElement(feedback, map_index);
130 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_cached_map));
131 GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)),
132 &next_entry);
133
134 // Found, now call handler.
135 TNode<MaybeObject> handler =
136 LoadWeakFixedArrayElement(feedback, handler_index);
137 *var_handler = handler;
138 Goto(if_handler);
139
140 BIND(&next_entry);
141 }
142 Goto(&loop);
143
144 // Loop from {kUnrolledIterations}*kEntrySize to {length}.
145 BIND(&loop);
146 Node* start_index = IntPtrConstant(kUnrolledIterations * kEntrySize);
147 Node* end_index = LoadAndUntagWeakFixedArrayLength(feedback);
148 BuildFastLoop(
149 start_index, end_index,
150 [this, receiver_map, feedback, if_handler, var_handler](Node* index) {
151 Label next_entry(this);
152 TNode<MaybeObject> maybe_cached_map =
153 LoadWeakFixedArrayElement(feedback, index);
154 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_cached_map));
155 GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)),
156 &next_entry);
157
158 // Found, now call handler.
159 TNode<MaybeObject> handler =
160 LoadWeakFixedArrayElement(feedback, index, kPointerSize);
161 *var_handler = handler;
162 Goto(if_handler);
163
164 BIND(&next_entry);
165 },
166 kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
167 // The loop falls through if no handler was found.
168 Goto(if_miss);
169 }
170
HandleLoadICHandlerCase(const LoadICParameters * p,TNode<Object> handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode,OnNonExistent on_nonexistent,ElementSupport support_elements)171 void AccessorAssembler::HandleLoadICHandlerCase(
172 const LoadICParameters* p, TNode<Object> handler, Label* miss,
173 ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent,
174 ElementSupport support_elements) {
175 Comment("have_handler");
176
177 VARIABLE(var_holder, MachineRepresentation::kTagged, p->holder);
178 VARIABLE(var_smi_handler, MachineRepresentation::kTagged, handler);
179
180 Variable* vars[] = {&var_holder, &var_smi_handler};
181 Label if_smi_handler(this, 2, vars);
182 Label try_proto_handler(this, Label::kDeferred),
183 call_handler(this, Label::kDeferred);
184
185 Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
186
187 // |handler| is a Smi, encoding what to do. See SmiHandler methods
188 // for the encoding format.
189 BIND(&if_smi_handler);
190 {
191 HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(),
192 handler, miss, exit_point, on_nonexistent,
193 support_elements);
194 }
195
196 BIND(&try_proto_handler);
197 {
198 GotoIf(IsCodeMap(LoadMap(CAST(handler))), &call_handler);
199 HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler,
200 &if_smi_handler, miss, exit_point, ic_mode);
201 }
202
203 BIND(&call_handler);
204 {
205 exit_point->ReturnCallStub(LoadWithVectorDescriptor{}, handler, p->context,
206 p->receiver, p->name, p->slot, p->vector);
207 }
208 }
209
HandleLoadCallbackProperty(const LoadICParameters * p,TNode<JSObject> holder,TNode<WordT> handler_word,ExitPoint * exit_point)210 void AccessorAssembler::HandleLoadCallbackProperty(const LoadICParameters* p,
211 TNode<JSObject> holder,
212 TNode<WordT> handler_word,
213 ExitPoint* exit_point) {
214 Comment("native_data_property_load");
215 Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
216
217 Label runtime(this, Label::kDeferred);
218 Callable callable = CodeFactory::ApiGetter(isolate());
219 TNode<AccessorInfo> accessor_info =
220 CAST(LoadDescriptorValue(LoadMap(holder), descriptor));
221
222 GotoIf(IsRuntimeCallStatsEnabled(), &runtime);
223 exit_point->ReturnCallStub(callable, p->context, p->receiver, holder,
224 accessor_info);
225
226 BIND(&runtime);
227 exit_point->ReturnCallRuntime(Runtime::kLoadCallbackProperty, p->context,
228 p->receiver, holder, accessor_info, p->name);
229 }
230
HandleLoadAccessor(const LoadICParameters * p,TNode<CallHandlerInfo> call_handler_info,TNode<WordT> handler_word,TNode<DataHandler> handler,TNode<IntPtrT> handler_kind,ExitPoint * exit_point)231 void AccessorAssembler::HandleLoadAccessor(
232 const LoadICParameters* p, TNode<CallHandlerInfo> call_handler_info,
233 TNode<WordT> handler_word, TNode<DataHandler> handler,
234 TNode<IntPtrT> handler_kind, ExitPoint* exit_point) {
235 Comment("api_getter");
236 Label runtime(this, Label::kDeferred);
237 // Context is stored either in data2 or data3 field depending on whether
238 // the access check is enabled for this handler or not.
239 TNode<MaybeObject> maybe_context = Select<MaybeObject>(
240 IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
241 [=] { return LoadHandlerDataField(handler, 3); },
242 [=] { return LoadHandlerDataField(handler, 2); });
243
244 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context));
245 CSA_CHECK(this, IsNotClearedWeakHeapObject(maybe_context));
246 TNode<Object> context = ToWeakHeapObject(maybe_context);
247
248 GotoIf(IsRuntimeCallStatsEnabled(), &runtime);
249 {
250 TNode<Foreign> foreign = CAST(
251 LoadObjectField(call_handler_info, CallHandlerInfo::kJsCallbackOffset));
252 TNode<WordT> callback = TNode<WordT>::UncheckedCast(LoadObjectField(
253 foreign, Foreign::kForeignAddressOffset, MachineType::Pointer()));
254 TNode<Object> data =
255 LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
256
257 VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver);
258 Label load(this);
259 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
260 &load);
261
262 CSA_ASSERT(
263 this,
264 WordEqual(handler_kind,
265 IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)));
266
267 api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver)));
268 Goto(&load);
269
270 BIND(&load);
271 Callable callable = CodeFactory::CallApiCallback(isolate(), 0);
272 exit_point->Return(CallStub(callable, nullptr, context, data,
273 api_holder.value(), callback, p->receiver));
274 }
275
276 BIND(&runtime);
277 exit_point->ReturnCallRuntime(Runtime::kLoadAccessorProperty, context,
278 p->receiver, SmiTag(handler_kind),
279 call_handler_info);
280 }
281
HandleLoadField(Node * holder,Node * handler_word,Variable * var_double_value,Label * rebox_double,ExitPoint * exit_point)282 void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word,
283 Variable* var_double_value,
284 Label* rebox_double,
285 ExitPoint* exit_point) {
286 Comment("field_load");
287 Node* index = DecodeWord<LoadHandler::FieldIndexBits>(handler_word);
288 Node* offset = IntPtrMul(index, IntPtrConstant(kPointerSize));
289
290 Label inobject(this), out_of_object(this);
291 Branch(IsSetWord<LoadHandler::IsInobjectBits>(handler_word), &inobject,
292 &out_of_object);
293
294 BIND(&inobject);
295 {
296 Label is_double(this);
297 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
298 exit_point->Return(LoadObjectField(holder, offset));
299
300 BIND(&is_double);
301 if (FLAG_unbox_double_fields) {
302 var_double_value->Bind(
303 LoadObjectField(holder, offset, MachineType::Float64()));
304 } else {
305 Node* mutable_heap_number = LoadObjectField(holder, offset);
306 var_double_value->Bind(LoadHeapNumberValue(mutable_heap_number));
307 }
308 Goto(rebox_double);
309 }
310
311 BIND(&out_of_object);
312 {
313 Label is_double(this);
314 Node* properties = LoadFastProperties(holder);
315 Node* value = LoadObjectField(properties, offset);
316 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
317 exit_point->Return(value);
318
319 BIND(&is_double);
320 var_double_value->Bind(LoadHeapNumberValue(value));
321 Goto(rebox_double);
322 }
323 }
324
LoadDescriptorValue(TNode<Map> map,Node * descriptor)325 TNode<Object> AccessorAssembler::LoadDescriptorValue(TNode<Map> map,
326 Node* descriptor) {
327 return CAST(LoadDescriptorValueOrFieldType(map, descriptor));
328 }
329
LoadDescriptorValueOrFieldType(TNode<Map> map,SloppyTNode<IntPtrT> descriptor)330 TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType(
331 TNode<Map> map, SloppyTNode<IntPtrT> descriptor) {
332 TNode<DescriptorArray> descriptors = LoadMapDescriptors(map);
333 TNode<IntPtrT> scaled_descriptor =
334 IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize));
335 TNode<IntPtrT> value_index = IntPtrAdd(
336 scaled_descriptor, IntPtrConstant(DescriptorArray::kFirstIndex +
337 DescriptorArray::kEntryValueIndex));
338 CSA_ASSERT(this, UintPtrLessThan(descriptor, LoadAndUntagWeakFixedArrayLength(
339 descriptors)));
340 return LoadWeakFixedArrayElement(descriptors, value_index);
341 }
342
HandleLoadICSmiHandlerCase(const LoadICParameters * p,Node * holder,SloppyTNode<Smi> smi_handler,SloppyTNode<Object> handler,Label * miss,ExitPoint * exit_point,OnNonExistent on_nonexistent,ElementSupport support_elements)343 void AccessorAssembler::HandleLoadICSmiHandlerCase(
344 const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler,
345 SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point,
346 OnNonExistent on_nonexistent, ElementSupport support_elements) {
347 VARIABLE(var_double_value, MachineRepresentation::kFloat64);
348 Label rebox_double(this, &var_double_value);
349
350 TNode<WordT> handler_word = SmiUntag(smi_handler);
351 TNode<IntPtrT> handler_kind =
352 Signed(DecodeWord<LoadHandler::KindBits>(handler_word));
353 if (support_elements == kSupportElements) {
354 Label if_element(this), if_indexed_string(this), if_property(this);
355 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
356 &if_element);
357 Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)),
358 &if_indexed_string, &if_property);
359
360 BIND(&if_element);
361 Comment("element_load");
362 Node* intptr_index = TryToIntptr(p->name, miss);
363 Node* elements = LoadElements(holder);
364 Node* is_jsarray_condition =
365 IsSetWord<LoadHandler::IsJsArrayBits>(handler_word);
366 Node* elements_kind =
367 DecodeWord32FromWord<LoadHandler::ElementsKindBits>(handler_word);
368 Label if_hole(this), unimplemented_elements_kind(this),
369 if_oob(this, Label::kDeferred);
370 EmitElementLoad(holder, elements, elements_kind, intptr_index,
371 is_jsarray_condition, &if_hole, &rebox_double,
372 &var_double_value, &unimplemented_elements_kind, &if_oob,
373 miss, exit_point);
374
375 BIND(&unimplemented_elements_kind);
376 {
377 // Smi handlers should only be installed for supported elements kinds.
378 // Crash if we get here.
379 DebugBreak();
380 Goto(miss);
381 }
382
383 BIND(&if_oob);
384 {
385 Comment("out of bounds elements access");
386 Label return_undefined(this);
387
388 // Check if we're allowed to handle OOB accesses.
389 Node* allow_out_of_bounds =
390 IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
391 GotoIfNot(allow_out_of_bounds, miss);
392
393 // Negative indices aren't valid array indices (according to
394 // the ECMAScript specification), and are stored as properties
395 // in V8, not elements. So we cannot handle them here, except
396 // in case of typed arrays, where integer indexed properties
397 // aren't looked up in the prototype chain.
398 GotoIf(IsJSTypedArray(holder), &return_undefined);
399 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), miss);
400
401 // For all other receivers we need to check that the prototype chain
402 // doesn't contain any elements.
403 BranchIfPrototypesHaveNoElements(LoadMap(holder), &return_undefined,
404 miss);
405
406 BIND(&return_undefined);
407 exit_point->Return(UndefinedConstant());
408 }
409
410 BIND(&if_hole);
411 {
412 Comment("convert hole");
413 GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
414 GotoIf(IsNoElementsProtectorCellInvalid(), miss);
415 exit_point->Return(UndefinedConstant());
416 }
417
418 BIND(&if_indexed_string);
419 {
420 Label if_oob(this, Label::kDeferred);
421
422 Comment("indexed string");
423 Node* intptr_index = TryToIntptr(p->name, miss);
424 Node* length = LoadStringLengthAsWord(holder);
425 GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob);
426 TNode<Int32T> code = StringCharCodeAt(holder, intptr_index);
427 TNode<String> result = StringFromSingleCharCode(code);
428 Return(result);
429
430 BIND(&if_oob);
431 Node* allow_out_of_bounds =
432 IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
433 GotoIfNot(allow_out_of_bounds, miss);
434 GotoIf(IsNoElementsProtectorCellInvalid(), miss);
435 Return(UndefinedConstant());
436 }
437
438 BIND(&if_property);
439 Comment("property_load");
440 }
441
442 Label constant(this), field(this), normal(this, Label::kDeferred),
443 interceptor(this, Label::kDeferred), nonexistent(this),
444 accessor(this, Label::kDeferred), global(this, Label::kDeferred),
445 module_export(this, Label::kDeferred), proxy(this, Label::kDeferred),
446 native_data_property(this), api_getter(this);
447 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
448
449 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
450 &constant);
451
452 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
453 &nonexistent);
454
455 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)),
456 &normal);
457
458 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
459 &accessor);
460
461 GotoIf(
462 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)),
463 &native_data_property);
464
465 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
466 &api_getter);
467
468 GotoIf(WordEqual(handler_kind,
469 IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)),
470 &api_getter);
471
472 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)),
473 &global);
474
475 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy);
476
477 Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kModuleExport)),
478 &module_export, &interceptor);
479
480 BIND(&field);
481 HandleLoadField(holder, handler_word, &var_double_value, &rebox_double,
482 exit_point);
483
484 BIND(&nonexistent);
485 // This is a handler for a load of a non-existent value.
486 if (on_nonexistent == OnNonExistent::kThrowReferenceError) {
487 exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
488 p->name);
489 } else {
490 DCHECK_EQ(OnNonExistent::kReturnUndefined, on_nonexistent);
491 exit_point->Return(UndefinedConstant());
492 }
493
494 BIND(&constant);
495 {
496 Comment("constant_load");
497 Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
498 Node* value = LoadDescriptorValue(LoadMap(holder), descriptor);
499
500 exit_point->Return(value);
501 }
502
503 BIND(&normal);
504 {
505 Comment("load_normal");
506 TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder));
507 TVARIABLE(IntPtrT, var_name_index);
508 Label found(this, &var_name_index);
509 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found,
510 &var_name_index, miss);
511 BIND(&found);
512 {
513 VARIABLE(var_details, MachineRepresentation::kWord32);
514 VARIABLE(var_value, MachineRepresentation::kTagged);
515 LoadPropertyFromNameDictionary(properties, var_name_index.value(),
516 &var_details, &var_value);
517 Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
518 p->context, p->receiver, miss);
519 exit_point->Return(value);
520 }
521 }
522
523 BIND(&accessor);
524 {
525 Comment("accessor_load");
526 Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word);
527 Node* accessor_pair = LoadDescriptorValue(LoadMap(holder), descriptor);
528 CSA_ASSERT(this, IsAccessorPair(accessor_pair));
529 Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
530 CSA_ASSERT(this, Word32BinaryNot(IsTheHole(getter)));
531
532 Callable callable = CodeFactory::Call(isolate());
533 exit_point->Return(CallJS(callable, p->context, getter, p->receiver));
534 }
535
536 BIND(&native_data_property);
537 HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point);
538
539 BIND(&api_getter);
540 HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler), handler_kind,
541 exit_point);
542
543 BIND(&proxy);
544 {
545 VARIABLE(var_index, MachineType::PointerRepresentation());
546 VARIABLE(var_unique, MachineRepresentation::kTagged);
547
548 Label if_index(this), if_unique_name(this),
549 to_name_failed(this, Label::kDeferred);
550
551 if (support_elements == kSupportElements) {
552 DCHECK_NE(on_nonexistent, OnNonExistent::kThrowReferenceError);
553
554 TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
555 &to_name_failed);
556
557 BIND(&if_unique_name);
558 exit_point->ReturnCallStub(
559 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
560 p->context, holder, var_unique.value(), p->receiver,
561 SmiConstant(on_nonexistent));
562
563 BIND(&if_index);
564 // TODO(mslekova): introduce TryToName that doesn't try to compute
565 // the intptr index value
566 Goto(&to_name_failed);
567
568 BIND(&to_name_failed);
569 exit_point->ReturnCallRuntime(Runtime::kGetPropertyWithReceiver,
570 p->context, holder, p->name, p->receiver,
571 SmiConstant(on_nonexistent));
572 } else {
573 exit_point->ReturnCallStub(
574 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
575 p->context, holder, p->name, p->receiver,
576 SmiConstant(on_nonexistent));
577 }
578 }
579
580 BIND(&global);
581 {
582 CSA_ASSERT(this, IsPropertyCell(holder));
583 // Ensure the property cell doesn't contain the hole.
584 Node* value = LoadObjectField(holder, PropertyCell::kValueOffset);
585 Node* details =
586 LoadAndUntagToWord32ObjectField(holder, PropertyCell::kDetailsOffset);
587 GotoIf(IsTheHole(value), miss);
588
589 exit_point->Return(
590 CallGetterIfAccessor(value, details, p->context, p->receiver, miss));
591 }
592
593 BIND(&interceptor);
594 {
595 Comment("load_interceptor");
596 exit_point->ReturnCallRuntime(Runtime::kLoadPropertyWithInterceptor,
597 p->context, p->name, p->receiver, holder,
598 p->slot, p->vector);
599 }
600
601 BIND(&module_export);
602 {
603 Comment("module export");
604 Node* index = DecodeWord<LoadHandler::ExportsIndexBits>(handler_word);
605 Node* module =
606 LoadObjectField(p->receiver, JSModuleNamespace::kModuleOffset,
607 MachineType::TaggedPointer());
608 TNode<ObjectHashTable> exports = CAST(LoadObjectField(
609 module, Module::kExportsOffset, MachineType::TaggedPointer()));
610 Node* cell = LoadFixedArrayElement(exports, index);
611 // The handler is only installed for exports that exist.
612 CSA_ASSERT(this, IsCell(cell));
613 Node* value = LoadCellValue(cell);
614 Label is_the_hole(this, Label::kDeferred);
615 GotoIf(IsTheHole(value), &is_the_hole);
616 exit_point->Return(value);
617
618 BIND(&is_the_hole);
619 {
620 Node* message = SmiConstant(MessageTemplate::kNotDefined);
621 exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
622 message, p->name);
623 }
624 }
625
626 BIND(&rebox_double);
627 exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value()));
628 }
629
630 // Performs actions common to both load and store handlers:
631 // 1. Checks prototype validity cell.
632 // 2. If |on_code_handler| is provided, then it checks if the sub handler is
633 // a smi or code and if it's a code then it calls |on_code_handler| to
634 // generate a code that handles Code handlers.
635 // If |on_code_handler| is not provided, then only smi sub handler are
636 // expected.
637 // 3. Does access check on receiver if ICHandler::DoAccessCheckOnReceiverBits
638 // bit is set in the smi handler.
639 // 4. Does dictionary lookup on receiver if ICHandler::LookupOnReceiverBits bit
640 // is set in the smi handler. If |on_found_on_receiver| is provided then
641 // it calls it to generate a code that handles the "found on receiver case"
642 // or just misses if the |on_found_on_receiver| is not provided.
643 // 5. Falls through in a case of a smi handler which is returned from this
644 // function (tagged!).
645 // TODO(ishell): Remove templatezation once we move common bits from
646 // Load/StoreHandler to the base class.
647 template <typename ICHandler, typename ICParameters>
HandleProtoHandler(const ICParameters * p,Node * handler,const OnCodeHandler & on_code_handler,const OnFoundOnReceiver & on_found_on_receiver,Label * miss,ICMode ic_mode)648 Node* AccessorAssembler::HandleProtoHandler(
649 const ICParameters* p, Node* handler, const OnCodeHandler& on_code_handler,
650 const OnFoundOnReceiver& on_found_on_receiver, Label* miss,
651 ICMode ic_mode) {
652 //
653 // Check prototype validity cell.
654 //
655 {
656 Node* maybe_validity_cell =
657 LoadObjectField(handler, ICHandler::kValidityCellOffset);
658 CheckPrototypeValidityCell(maybe_validity_cell, miss);
659 }
660
661 //
662 // Check smi handler bits.
663 //
664 {
665 Node* smi_or_code_handler =
666 LoadObjectField(handler, ICHandler::kSmiHandlerOffset);
667 if (on_code_handler) {
668 Label if_smi_handler(this);
669 GotoIf(TaggedIsSmi(smi_or_code_handler), &if_smi_handler);
670
671 CSA_ASSERT(this, IsCodeMap(LoadMap(smi_or_code_handler)));
672 on_code_handler(smi_or_code_handler);
673
674 BIND(&if_smi_handler);
675 } else {
676 CSA_ASSERT(this, TaggedIsSmi(smi_or_code_handler));
677 }
678 Node* handler_flags = SmiUntag(smi_or_code_handler);
679
680 // Lookup on receiver and access checks are not necessary for global ICs
681 // because in the former case the validity cell check guards modifications
682 // of the global object and the latter is not applicable to the global
683 // object.
684 int mask = ICHandler::LookupOnReceiverBits::kMask |
685 ICHandler::DoAccessCheckOnReceiverBits::kMask;
686 if (ic_mode == ICMode::kGlobalIC) {
687 CSA_ASSERT(this, IsClearWord(handler_flags, mask));
688 } else {
689 DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode);
690
691 Label done(this), if_do_access_check(this), if_lookup_on_receiver(this);
692 GotoIf(IsClearWord(handler_flags, mask), &done);
693 // Only one of the bits can be set at a time.
694 CSA_ASSERT(this,
695 WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)),
696 IntPtrConstant(mask)));
697 Branch(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
698 &if_do_access_check, &if_lookup_on_receiver);
699
700 BIND(&if_do_access_check);
701 {
702 TNode<MaybeObject> data2 = LoadHandlerDataField(handler, 2);
703 CSA_ASSERT(this, IsWeakOrClearedHeapObject(data2));
704 TNode<Object> expected_native_context = ToWeakHeapObject(data2, miss);
705 EmitAccessCheck(expected_native_context, p->context, p->receiver, &done,
706 miss);
707 }
708
709 // Dictionary lookup on receiver is not necessary for Load/StoreGlobalIC
710 // because prototype validity cell check already guards modifications of
711 // the global object.
712 BIND(&if_lookup_on_receiver);
713 {
714 DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode);
715 CSA_ASSERT(this, Word32BinaryNot(HasInstanceType(
716 p->receiver, JS_GLOBAL_OBJECT_TYPE)));
717
718 TNode<NameDictionary> properties =
719 CAST(LoadSlowProperties(p->receiver));
720 TVARIABLE(IntPtrT, var_name_index);
721 Label found(this, &var_name_index);
722 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found,
723 &var_name_index, &done);
724 BIND(&found);
725 {
726 if (on_found_on_receiver) {
727 on_found_on_receiver(properties, var_name_index.value());
728 } else {
729 Goto(miss);
730 }
731 }
732 }
733
734 BIND(&done);
735 }
736 return smi_or_code_handler;
737 }
738 }
739
HandleLoadICProtoHandler(const LoadICParameters * p,Node * handler,Variable * var_holder,Variable * var_smi_handler,Label * if_smi_handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode)740 void AccessorAssembler::HandleLoadICProtoHandler(
741 const LoadICParameters* p, Node* handler, Variable* var_holder,
742 Variable* var_smi_handler, Label* if_smi_handler, Label* miss,
743 ExitPoint* exit_point, ICMode ic_mode) {
744 DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
745 DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
746
747 Node* smi_handler = HandleProtoHandler<LoadHandler>(
748 p, handler,
749 // Code sub-handlers are not expected in LoadICs, so no |on_code_handler|.
750 nullptr,
751 // on_found_on_receiver
752 [=](Node* properties, Node* name_index) {
753 VARIABLE(var_details, MachineRepresentation::kWord32);
754 VARIABLE(var_value, MachineRepresentation::kTagged);
755 LoadPropertyFromNameDictionary(properties, name_index, &var_details,
756 &var_value);
757 Node* value =
758 CallGetterIfAccessor(var_value.value(), var_details.value(),
759 p->context, p->receiver, miss);
760 exit_point->Return(value);
761 },
762 miss, ic_mode);
763
764 TNode<MaybeObject> maybe_holder = LoadHandlerDataField(handler, 1);
765
766 Label load_from_cached_holder(this), done(this);
767
768 Branch(IsStrongReferenceTo(maybe_holder, NullConstant()), &done,
769 &load_from_cached_holder);
770
771 BIND(&load_from_cached_holder);
772 {
773 // For regular holders, having passed the receiver map check and the
774 // validity cell check implies that |holder| is alive. However, for global
775 // object receivers, |maybe_holder| may be cleared.
776 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_holder));
777 Node* holder = ToWeakHeapObject(maybe_holder, miss);
778
779 var_holder->Bind(holder);
780 Goto(&done);
781 }
782
783 BIND(&done);
784 {
785 var_smi_handler->Bind(smi_handler);
786 Goto(if_smi_handler);
787 }
788 }
789
EmitAccessCheck(Node * expected_native_context,Node * context,Node * receiver,Label * can_access,Label * miss)790 void AccessorAssembler::EmitAccessCheck(Node* expected_native_context,
791 Node* context, Node* receiver,
792 Label* can_access, Label* miss) {
793 CSA_ASSERT(this, IsNativeContext(expected_native_context));
794
795 Node* native_context = LoadNativeContext(context);
796 GotoIf(WordEqual(expected_native_context, native_context), can_access);
797 // If the receiver is not a JSGlobalProxy then we miss.
798 GotoIfNot(IsJSGlobalProxy(receiver), miss);
799 // For JSGlobalProxy receiver try to compare security tokens of current
800 // and expected native contexts.
801 Node* expected_token = LoadContextElement(expected_native_context,
802 Context::SECURITY_TOKEN_INDEX);
803 Node* current_token =
804 LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
805 Branch(WordEqual(expected_token, current_token), can_access, miss);
806 }
807
JumpIfDataProperty(Node * details,Label * writable,Label * readonly)808 void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
809 Label* readonly) {
810 // Accessor properties never have the READ_ONLY attribute set.
811 GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
812 readonly);
813 Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
814 GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
815 // Fall through if it's an accessor property.
816 }
817
HandleStoreICNativeDataProperty(const StoreICParameters * p,Node * holder,Node * handler_word)818 void AccessorAssembler::HandleStoreICNativeDataProperty(
819 const StoreICParameters* p, Node* holder, Node* handler_word) {
820 Comment("native_data_property_store");
821 Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
822 Node* accessor_info = LoadDescriptorValue(LoadMap(holder), descriptor);
823 CSA_CHECK(this, IsAccessorInfo(accessor_info));
824
825 Node* language_mode = GetLanguageMode(p->vector, p->slot);
826
827 TailCallRuntime(Runtime::kStoreCallbackProperty, p->context, p->receiver,
828 holder, accessor_info, p->name, p->value, language_mode);
829 }
830
HandleStoreICHandlerCase(const StoreICParameters * p,TNode<MaybeObject> handler,Label * miss,ICMode ic_mode,ElementSupport support_elements)831 void AccessorAssembler::HandleStoreICHandlerCase(
832 const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss,
833 ICMode ic_mode, ElementSupport support_elements) {
834 Label if_smi_handler(this), if_nonsmi_handler(this);
835 Label if_proto_handler(this), if_element_handler(this), call_handler(this),
836 store_transition_or_global(this);
837
838 Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler);
839
840 // |handler| is a Smi, encoding what to do. See SmiHandler methods
841 // for the encoding format.
842 BIND(&if_smi_handler);
843 {
844 Node* holder = p->receiver;
845 Node* handler_word = SmiUntag(CAST(handler));
846
847 Label if_fast_smi(this), if_proxy(this);
848
849 STATIC_ASSERT(StoreHandler::kGlobalProxy + 1 == StoreHandler::kNormal);
850 STATIC_ASSERT(StoreHandler::kNormal + 1 == StoreHandler::kProxy);
851 STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber);
852
853 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
854 GotoIf(IntPtrLessThan(handler_kind,
855 IntPtrConstant(StoreHandler::kGlobalProxy)),
856 &if_fast_smi);
857 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)),
858 &if_proxy);
859 CSA_ASSERT(this,
860 WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNormal)));
861 TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder));
862
863 TVARIABLE(IntPtrT, var_name_index);
864 Label dictionary_found(this, &var_name_index);
865 NameDictionaryLookup<NameDictionary>(
866 properties, CAST(p->name), &dictionary_found, &var_name_index, miss);
867 BIND(&dictionary_found);
868 {
869 Node* details = LoadDetailsByKeyIndex<NameDictionary>(
870 properties, var_name_index.value());
871 // Check that the property is a writable data property (no accessor).
872 const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask |
873 PropertyDetails::kAttributesReadOnlyMask;
874 STATIC_ASSERT(kData == 0);
875 GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
876
877 StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(),
878 p->value);
879 Return(p->value);
880 }
881
882 BIND(&if_fast_smi);
883 {
884 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
885
886 Label data(this), accessor(this), native_data_property(this);
887 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kAccessor)),
888 &accessor);
889 Branch(WordEqual(handler_kind,
890 IntPtrConstant(StoreHandler::kNativeDataProperty)),
891 &native_data_property, &data);
892
893 BIND(&accessor);
894 HandleStoreAccessor(p, holder, handler_word);
895
896 BIND(&native_data_property);
897 HandleStoreICNativeDataProperty(p, holder, handler_word);
898
899 BIND(&data);
900 // Handle non-transitioning field stores.
901 HandleStoreICSmiHandlerCase(handler_word, holder, p->value, miss);
902 }
903
904 BIND(&if_proxy);
905 HandleStoreToProxy(p, holder, miss, support_elements);
906 }
907
908 BIND(&if_nonsmi_handler);
909 {
910 GotoIf(IsWeakOrClearedHeapObject(handler), &store_transition_or_global);
911 TNode<HeapObject> strong_handler = CAST(handler);
912 TNode<Map> handler_map = LoadMap(strong_handler);
913 Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler);
914
915 BIND(&if_proto_handler);
916 {
917 HandleStoreICProtoHandler(p, CAST(strong_handler), miss, ic_mode,
918 support_elements);
919 }
920
921 // |handler| is a heap object. Must be code, call it.
922 BIND(&call_handler);
923 {
924 TailCallStub(StoreWithVectorDescriptor{}, CAST(strong_handler),
925 CAST(p->context), p->receiver, p->name, p->value, p->slot,
926 p->vector);
927 }
928 }
929
930 BIND(&store_transition_or_global);
931 {
932 // Load value or miss if the {handler} weak cell is cleared.
933 CSA_ASSERT(this, IsWeakOrClearedHeapObject(handler));
934 TNode<HeapObject> map_or_property_cell = ToWeakHeapObject(handler, miss);
935
936 Label store_global(this), store_transition(this);
937 Branch(IsMap(map_or_property_cell), &store_transition, &store_global);
938
939 BIND(&store_global);
940 {
941 TNode<PropertyCell> property_cell = CAST(map_or_property_cell);
942 ExitPoint direct_exit(this);
943 StoreGlobalIC_PropertyCellCase(property_cell, p->value, &direct_exit,
944 miss);
945 }
946 BIND(&store_transition);
947 {
948 TNode<Map> map = CAST(map_or_property_cell);
949 HandleStoreICTransitionMapHandlerCase(p, map, miss, false);
950 Return(p->value);
951 }
952 }
953 }
954
HandleStoreICTransitionMapHandlerCase(const StoreICParameters * p,TNode<Map> transition_map,Label * miss,bool validate_transition_handler)955 void AccessorAssembler::HandleStoreICTransitionMapHandlerCase(
956 const StoreICParameters* p, TNode<Map> transition_map, Label* miss,
957 bool validate_transition_handler) {
958 Node* maybe_validity_cell =
959 LoadObjectField(transition_map, Map::kPrototypeValidityCellOffset);
960 CheckPrototypeValidityCell(maybe_validity_cell, miss);
961
962 TNode<Uint32T> bitfield3 = LoadMapBitField3(transition_map);
963 CSA_ASSERT(this, IsClearWord32<Map::IsDictionaryMapBit>(bitfield3));
964 GotoIf(IsSetWord32<Map::IsDeprecatedBit>(bitfield3), miss);
965
966 // Load last descriptor details.
967 Node* nof = DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
968 CSA_ASSERT(this, WordNotEqual(nof, IntPtrConstant(0)));
969 TNode<DescriptorArray> descriptors = LoadMapDescriptors(transition_map);
970
971 Node* factor = IntPtrConstant(DescriptorArray::kEntrySize);
972 TNode<IntPtrT> last_key_index = UncheckedCast<IntPtrT>(IntPtrAdd(
973 IntPtrConstant(DescriptorArray::ToKeyIndex(-1)), IntPtrMul(nof, factor)));
974 if (validate_transition_handler) {
975 Node* key = LoadWeakFixedArrayElement(descriptors, last_key_index);
976 GotoIf(WordNotEqual(key, p->name), miss);
977 } else {
978 CSA_ASSERT(this,
979 WordEqual(BitcastMaybeObjectToWord(LoadWeakFixedArrayElement(
980 descriptors, last_key_index)),
981 p->name));
982 }
983 Node* details = LoadDetailsByKeyIndex(descriptors, last_key_index);
984 if (validate_transition_handler) {
985 // Follow transitions only in the following cases:
986 // 1) name is a non-private symbol and attributes equal to NONE,
987 // 2) name is a private symbol and attributes equal to DONT_ENUM.
988 Label attributes_ok(this);
989 const int kAttributesDontDeleteReadOnlyMask =
990 PropertyDetails::kAttributesDontDeleteMask |
991 PropertyDetails::kAttributesReadOnlyMask;
992 // Both DontDelete and ReadOnly attributes must not be set.
993 GotoIf(IsSetWord32(details, kAttributesDontDeleteReadOnlyMask), miss);
994
995 // DontEnum attribute is allowed only for private symbols and vice versa.
996 Branch(Word32Equal(
997 IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
998 IsPrivateSymbol(p->name)),
999 &attributes_ok, miss);
1000
1001 BIND(&attributes_ok);
1002 }
1003
1004 OverwriteExistingFastDataProperty(p->receiver, transition_map, descriptors,
1005 last_key_index, details, p->value, miss,
1006 true);
1007 }
1008
CheckFieldType(TNode<DescriptorArray> descriptors,Node * name_index,Node * representation,Node * value,Label * bailout)1009 void AccessorAssembler::CheckFieldType(TNode<DescriptorArray> descriptors,
1010 Node* name_index, Node* representation,
1011 Node* value, Label* bailout) {
1012 Label r_smi(this), r_double(this), r_heapobject(this), all_fine(this);
1013 // Ignore FLAG_track_fields etc. and always emit code for all checks,
1014 // because this builtin is part of the snapshot and therefore should
1015 // be flag independent.
1016 GotoIf(Word32Equal(representation, Int32Constant(Representation::kSmi)),
1017 &r_smi);
1018 GotoIf(Word32Equal(representation, Int32Constant(Representation::kDouble)),
1019 &r_double);
1020 GotoIf(
1021 Word32Equal(representation, Int32Constant(Representation::kHeapObject)),
1022 &r_heapobject);
1023 GotoIf(Word32Equal(representation, Int32Constant(Representation::kNone)),
1024 bailout);
1025 CSA_ASSERT(this, Word32Equal(representation,
1026 Int32Constant(Representation::kTagged)));
1027 Goto(&all_fine);
1028
1029 BIND(&r_smi);
1030 { Branch(TaggedIsSmi(value), &all_fine, bailout); }
1031
1032 BIND(&r_double);
1033 {
1034 GotoIf(TaggedIsSmi(value), &all_fine);
1035 Node* value_map = LoadMap(value);
1036 // While supporting mutable HeapNumbers would be straightforward, such
1037 // objects should not end up here anyway.
1038 CSA_ASSERT(this,
1039 WordNotEqual(value_map,
1040 LoadRoot(Heap::kMutableHeapNumberMapRootIndex)));
1041 Branch(IsHeapNumberMap(value_map), &all_fine, bailout);
1042 }
1043
1044 BIND(&r_heapobject);
1045 {
1046 GotoIf(TaggedIsSmi(value), bailout);
1047 TNode<MaybeObject> field_type = LoadFieldTypeByKeyIndex(
1048 descriptors, UncheckedCast<IntPtrT>(name_index));
1049 intptr_t kNoneType = reinterpret_cast<intptr_t>(FieldType::None());
1050 intptr_t kAnyType = reinterpret_cast<intptr_t>(FieldType::Any());
1051 DCHECK_NE(kNoneType, kClearedWeakHeapObject);
1052 DCHECK_NE(kAnyType, kClearedWeakHeapObject);
1053 // FieldType::None can't hold any value.
1054 GotoIf(WordEqual(BitcastMaybeObjectToWord(field_type),
1055 IntPtrConstant(kNoneType)),
1056 bailout);
1057 // FieldType::Any can hold any value.
1058 GotoIf(WordEqual(BitcastMaybeObjectToWord(field_type),
1059 IntPtrConstant(kAnyType)),
1060 &all_fine);
1061 // Cleared weak references count as FieldType::None, which can't hold any
1062 // value.
1063 TNode<Map> field_type_map = CAST(ToWeakHeapObject(field_type, bailout));
1064 // FieldType::Class(...) performs a map check.
1065 Branch(WordEqual(LoadMap(value), field_type_map), &all_fine, bailout);
1066 }
1067
1068 BIND(&all_fine);
1069 }
1070
OverwriteExistingFastDataProperty(Node * object,Node * object_map,Node * descriptors,Node * descriptor_name_index,Node * details,Node * value,Label * slow,bool do_transitioning_store)1071 void AccessorAssembler::OverwriteExistingFastDataProperty(
1072 Node* object, Node* object_map, Node* descriptors,
1073 Node* descriptor_name_index, Node* details, Node* value, Label* slow,
1074 bool do_transitioning_store) {
1075 Label done(this), if_field(this), if_descriptor(this);
1076
1077 CSA_ASSERT(this,
1078 Word32Equal(DecodeWord32<PropertyDetails::KindField>(details),
1079 Int32Constant(kData)));
1080
1081 Branch(Word32Equal(DecodeWord32<PropertyDetails::LocationField>(details),
1082 Int32Constant(kField)),
1083 &if_field, &if_descriptor);
1084
1085 BIND(&if_field);
1086 {
1087 if (FLAG_track_constant_fields && !do_transitioning_store) {
1088 // TODO(ishell): Taking the slow path is not necessary if new and old
1089 // values are identical.
1090 GotoIf(Word32Equal(
1091 DecodeWord32<PropertyDetails::ConstnessField>(details),
1092 Int32Constant(static_cast<int32_t>(VariableMode::kConst))),
1093 slow);
1094 }
1095
1096 Node* representation =
1097 DecodeWord32<PropertyDetails::RepresentationField>(details);
1098
1099 CheckFieldType(CAST(descriptors), descriptor_name_index, representation,
1100 value, slow);
1101
1102 Node* field_index =
1103 DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details);
1104 field_index = IntPtrAdd(field_index,
1105 LoadMapInobjectPropertiesStartInWords(object_map));
1106 Node* instance_size_in_words = LoadMapInstanceSizeInWords(object_map);
1107
1108 Label inobject(this), backing_store(this);
1109 Branch(UintPtrLessThan(field_index, instance_size_in_words), &inobject,
1110 &backing_store);
1111
1112 BIND(&inobject);
1113 {
1114 Node* field_offset = TimesPointerSize(field_index);
1115 Label tagged_rep(this), double_rep(this);
1116 Branch(
1117 Word32Equal(representation, Int32Constant(Representation::kDouble)),
1118 &double_rep, &tagged_rep);
1119 BIND(&double_rep);
1120 {
1121 Node* double_value = ChangeNumberToFloat64(value);
1122 if (FLAG_unbox_double_fields) {
1123 if (do_transitioning_store) {
1124 StoreMap(object, object_map);
1125 }
1126 StoreObjectFieldNoWriteBarrier(object, field_offset, double_value,
1127 MachineRepresentation::kFloat64);
1128 } else {
1129 if (do_transitioning_store) {
1130 Node* mutable_heap_number =
1131 AllocateMutableHeapNumberWithValue(double_value);
1132 StoreMap(object, object_map);
1133 StoreObjectField(object, field_offset, mutable_heap_number);
1134 } else {
1135 Node* mutable_heap_number = LoadObjectField(object, field_offset);
1136 StoreHeapNumberValue(mutable_heap_number, double_value);
1137 }
1138 }
1139 Goto(&done);
1140 }
1141
1142 BIND(&tagged_rep);
1143 {
1144 if (do_transitioning_store) {
1145 StoreMap(object, object_map);
1146 }
1147 StoreObjectField(object, field_offset, value);
1148 Goto(&done);
1149 }
1150 }
1151
1152 BIND(&backing_store);
1153 {
1154 Node* backing_store_index =
1155 IntPtrSub(field_index, instance_size_in_words);
1156
1157 if (do_transitioning_store) {
1158 // Allocate mutable heap number before extending properties backing
1159 // store to ensure that heap verifier will not see the heap in
1160 // inconsistent state.
1161 VARIABLE(var_value, MachineRepresentation::kTagged, value);
1162 {
1163 Label cont(this);
1164 GotoIf(Word32NotEqual(representation,
1165 Int32Constant(Representation::kDouble)),
1166 &cont);
1167 {
1168 Node* double_value = ChangeNumberToFloat64(value);
1169 Node* mutable_heap_number =
1170 AllocateMutableHeapNumberWithValue(double_value);
1171 var_value.Bind(mutable_heap_number);
1172 Goto(&cont);
1173 }
1174 BIND(&cont);
1175 }
1176
1177 TNode<PropertyArray> properties =
1178 CAST(ExtendPropertiesBackingStore(object, backing_store_index));
1179 StorePropertyArrayElement(properties, backing_store_index,
1180 var_value.value());
1181 StoreMap(object, object_map);
1182 Goto(&done);
1183
1184 } else {
1185 Label tagged_rep(this), double_rep(this);
1186 TNode<PropertyArray> properties = CAST(LoadFastProperties(object));
1187 Branch(
1188 Word32Equal(representation, Int32Constant(Representation::kDouble)),
1189 &double_rep, &tagged_rep);
1190 BIND(&double_rep);
1191 {
1192 Node* mutable_heap_number =
1193 LoadPropertyArrayElement(properties, backing_store_index);
1194 Node* double_value = ChangeNumberToFloat64(value);
1195 StoreHeapNumberValue(mutable_heap_number, double_value);
1196 Goto(&done);
1197 }
1198 BIND(&tagged_rep);
1199 {
1200 StorePropertyArrayElement(properties, backing_store_index, value);
1201 Goto(&done);
1202 }
1203 }
1204 }
1205 }
1206
1207 BIND(&if_descriptor);
1208 {
1209 // Check that constant matches value.
1210 Node* constant = LoadValueByKeyIndex(
1211 CAST(descriptors), UncheckedCast<IntPtrT>(descriptor_name_index));
1212 GotoIf(WordNotEqual(value, constant), slow);
1213
1214 if (do_transitioning_store) {
1215 StoreMap(object, object_map);
1216 }
1217 Goto(&done);
1218 }
1219 BIND(&done);
1220 }
1221
CheckPrototypeValidityCell(Node * maybe_validity_cell,Label * miss)1222 void AccessorAssembler::CheckPrototypeValidityCell(Node* maybe_validity_cell,
1223 Label* miss) {
1224 Label done(this);
1225 GotoIf(WordEqual(maybe_validity_cell, SmiConstant(Map::kPrototypeChainValid)),
1226 &done);
1227 CSA_ASSERT(this, TaggedIsNotSmi(maybe_validity_cell));
1228
1229 Node* cell_value = LoadObjectField(maybe_validity_cell, Cell::kValueOffset);
1230 Branch(WordEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)), &done,
1231 miss);
1232
1233 BIND(&done);
1234 }
1235
HandleStoreAccessor(const StoreICParameters * p,Node * holder,Node * handler_word)1236 void AccessorAssembler::HandleStoreAccessor(const StoreICParameters* p,
1237 Node* holder, Node* handler_word) {
1238 Comment("accessor_store");
1239 Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
1240 Node* accessor_pair = LoadDescriptorValue(LoadMap(holder), descriptor);
1241 CSA_ASSERT(this, IsAccessorPair(accessor_pair));
1242 Node* setter = LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
1243 CSA_ASSERT(this, Word32BinaryNot(IsTheHole(setter)));
1244
1245 Callable callable = CodeFactory::Call(isolate());
1246 Return(CallJS(callable, p->context, setter, p->receiver, p->value));
1247 }
1248
HandleStoreICProtoHandler(const StoreICParameters * p,TNode<StoreHandler> handler,Label * miss,ICMode ic_mode,ElementSupport support_elements)1249 void AccessorAssembler::HandleStoreICProtoHandler(
1250 const StoreICParameters* p, TNode<StoreHandler> handler, Label* miss,
1251 ICMode ic_mode, ElementSupport support_elements) {
1252 Comment("HandleStoreICProtoHandler");
1253
1254 OnCodeHandler on_code_handler;
1255 if (support_elements == kSupportElements) {
1256 // Code sub-handlers are expected only in KeyedStoreICs.
1257 on_code_handler = [=](Node* code_handler) {
1258 // This is either element store or transitioning element store.
1259 Label if_element_store(this), if_transitioning_element_store(this);
1260 Branch(IsStoreHandler0Map(LoadMap(handler)), &if_element_store,
1261 &if_transitioning_element_store);
1262 BIND(&if_element_store);
1263 {
1264 TailCallStub(StoreWithVectorDescriptor{}, code_handler, p->context,
1265 p->receiver, p->name, p->value, p->slot, p->vector);
1266 }
1267
1268 BIND(&if_transitioning_element_store);
1269 {
1270 TNode<MaybeObject> maybe_transition_map =
1271 LoadHandlerDataField(handler, 1);
1272 TNode<Map> transition_map =
1273 CAST(ToWeakHeapObject(maybe_transition_map, miss));
1274
1275 GotoIf(IsDeprecatedMap(transition_map), miss);
1276
1277 TailCallStub(StoreTransitionDescriptor{}, code_handler, p->context,
1278 p->receiver, p->name, transition_map, p->value, p->slot,
1279 p->vector);
1280 }
1281 };
1282 }
1283
1284 Node* smi_handler = HandleProtoHandler<StoreHandler>(
1285 p, handler, on_code_handler,
1286 // on_found_on_receiver
1287 [=](Node* properties, Node* name_index) {
1288 Node* details =
1289 LoadDetailsByKeyIndex<NameDictionary>(properties, name_index);
1290 // Check that the property is a writable data property (no accessor).
1291 const int kTypeAndReadOnlyMask =
1292 PropertyDetails::KindField::kMask |
1293 PropertyDetails::kAttributesReadOnlyMask;
1294 STATIC_ASSERT(kData == 0);
1295 GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
1296
1297 StoreValueByKeyIndex<NameDictionary>(
1298 CAST(properties), UncheckedCast<IntPtrT>(name_index), p->value);
1299 Return(p->value);
1300 },
1301 miss, ic_mode);
1302
1303 {
1304 Label if_add_normal(this), if_store_global_proxy(this), if_api_setter(this),
1305 if_accessor(this), if_native_data_property(this);
1306
1307 CSA_ASSERT(this, TaggedIsSmi(smi_handler));
1308 Node* handler_word = SmiUntag(smi_handler);
1309
1310 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
1311 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNormal)),
1312 &if_add_normal);
1313
1314 TNode<MaybeObject> maybe_holder = LoadHandlerDataField(handler, 1);
1315 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_holder));
1316 TNode<Object> holder = ToWeakHeapObject(maybe_holder, miss);
1317
1318 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kGlobalProxy)),
1319 &if_store_global_proxy);
1320
1321 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kAccessor)),
1322 &if_accessor);
1323
1324 GotoIf(WordEqual(handler_kind,
1325 IntPtrConstant(StoreHandler::kNativeDataProperty)),
1326 &if_native_data_property);
1327
1328 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetter)),
1329 &if_api_setter);
1330
1331 GotoIf(WordEqual(handler_kind,
1332 IntPtrConstant(StoreHandler::kApiSetterHolderIsPrototype)),
1333 &if_api_setter);
1334
1335 CSA_ASSERT(this,
1336 WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)));
1337 HandleStoreToProxy(p, holder, miss, support_elements);
1338
1339 BIND(&if_add_normal);
1340 {
1341 // This is a case of "transitioning store" to a dictionary mode object
1342 // when the property is still does not exist. The "existing property"
1343 // case is covered above by LookupOnReceiver bit handling of the smi
1344 // handler.
1345 Label slow(this);
1346 TNode<Map> receiver_map = LoadMap(p->receiver);
1347 InvalidateValidityCellIfPrototype(receiver_map);
1348
1349 TNode<NameDictionary> properties = CAST(LoadSlowProperties(p->receiver));
1350 Add<NameDictionary>(properties, CAST(p->name), p->value, &slow);
1351 Return(p->value);
1352
1353 BIND(&slow);
1354 TailCallRuntime(Runtime::kAddDictionaryProperty, p->context, p->receiver,
1355 p->name, p->value);
1356 }
1357
1358 BIND(&if_accessor);
1359 HandleStoreAccessor(p, holder, handler_word);
1360
1361 BIND(&if_native_data_property);
1362 HandleStoreICNativeDataProperty(p, holder, handler_word);
1363
1364 BIND(&if_api_setter);
1365 {
1366 Comment("api_setter");
1367 CSA_ASSERT(this, TaggedIsNotSmi(handler));
1368 Node* call_handler_info = holder;
1369
1370 // Context is stored either in data2 or data3 field depending on whether
1371 // the access check is enabled for this handler or not.
1372 TNode<MaybeObject> maybe_context = Select<MaybeObject>(
1373 IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word),
1374 [=] { return LoadHandlerDataField(handler, 3); },
1375 [=] { return LoadHandlerDataField(handler, 2); });
1376
1377 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context));
1378 TNode<Object> context =
1379 Select<Object>(IsClearedWeakHeapObject(maybe_context),
1380 [=] { return SmiConstant(0); },
1381 [=] { return ToWeakHeapObject(maybe_context); });
1382
1383 Node* foreign = LoadObjectField(call_handler_info,
1384 CallHandlerInfo::kJsCallbackOffset);
1385 Node* callback = LoadObjectField(foreign, Foreign::kForeignAddressOffset,
1386 MachineType::Pointer());
1387 Node* data =
1388 LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
1389
1390 VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver);
1391 Label store(this);
1392 GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetter)),
1393 &store);
1394
1395 CSA_ASSERT(
1396 this,
1397 WordEqual(handler_kind,
1398 IntPtrConstant(StoreHandler::kApiSetterHolderIsPrototype)));
1399
1400 api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver)));
1401 Goto(&store);
1402
1403 BIND(&store);
1404 Callable callable = CodeFactory::CallApiCallback(isolate(), 1);
1405 Return(CallStub(callable, nullptr, context, data, api_holder.value(),
1406 callback, p->receiver, p->value));
1407 }
1408
1409 BIND(&if_store_global_proxy);
1410 {
1411 ExitPoint direct_exit(this);
1412 StoreGlobalIC_PropertyCellCase(holder, p->value, &direct_exit, miss);
1413 }
1414 }
1415 }
1416
GetLanguageMode(Node * vector,Node * slot)1417 Node* AccessorAssembler::GetLanguageMode(Node* vector, Node* slot) {
1418 VARIABLE(var_language_mode, MachineRepresentation::kTaggedSigned,
1419 SmiConstant(LanguageMode::kStrict));
1420 Label language_mode_determined(this);
1421 BranchIfStrictMode(vector, slot, &language_mode_determined);
1422 var_language_mode.Bind(SmiConstant(LanguageMode::kSloppy));
1423 Goto(&language_mode_determined);
1424 BIND(&language_mode_determined);
1425 return var_language_mode.value();
1426 }
1427
HandleStoreToProxy(const StoreICParameters * p,Node * proxy,Label * miss,ElementSupport support_elements)1428 void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p,
1429 Node* proxy, Label* miss,
1430 ElementSupport support_elements) {
1431 VARIABLE(var_index, MachineType::PointerRepresentation());
1432 VARIABLE(var_unique, MachineRepresentation::kTagged);
1433
1434 Label if_index(this), if_unique_name(this),
1435 to_name_failed(this, Label::kDeferred);
1436
1437 Node* language_mode = GetLanguageMode(p->vector, p->slot);
1438
1439 if (support_elements == kSupportElements) {
1440 TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
1441 &to_name_failed);
1442
1443 BIND(&if_unique_name);
1444 CallBuiltin(Builtins::kProxySetProperty, p->context, proxy,
1445 var_unique.value(), p->value, p->receiver, language_mode);
1446 Return(p->value);
1447
1448 // The index case is handled earlier by the runtime.
1449 BIND(&if_index);
1450 // TODO(mslekova): introduce TryToName that doesn't try to compute
1451 // the intptr index value
1452 Goto(&to_name_failed);
1453
1454 BIND(&to_name_failed);
1455 TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy,
1456 p->name, p->value, p->receiver, language_mode);
1457 } else {
1458 Node* name = ToName(p->context, p->name);
1459 TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name,
1460 p->value, p->receiver, language_mode);
1461 }
1462 }
1463
HandleStoreICSmiHandlerCase(Node * handler_word,Node * holder,Node * value,Label * miss)1464 void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
1465 Node* holder, Node* value,
1466 Label* miss) {
1467 Comment("field store");
1468 #ifdef DEBUG
1469 Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
1470 if (FLAG_track_constant_fields) {
1471 CSA_ASSERT(
1472 this,
1473 Word32Or(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kField)),
1474 WordEqual(handler_kind,
1475 IntPtrConstant(StoreHandler::kConstField))));
1476 } else {
1477 CSA_ASSERT(this,
1478 WordEqual(handler_kind, IntPtrConstant(StoreHandler::kField)));
1479 }
1480 #endif
1481
1482 Node* field_representation =
1483 DecodeWord<StoreHandler::FieldRepresentationBits>(handler_word);
1484
1485 Label if_smi_field(this), if_double_field(this), if_heap_object_field(this),
1486 if_tagged_field(this);
1487
1488 GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kTagged)),
1489 &if_tagged_field);
1490 GotoIf(WordEqual(field_representation,
1491 IntPtrConstant(StoreHandler::kHeapObject)),
1492 &if_heap_object_field);
1493 GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kDouble)),
1494 &if_double_field);
1495 CSA_ASSERT(this, WordEqual(field_representation,
1496 IntPtrConstant(StoreHandler::kSmi)));
1497 Goto(&if_smi_field);
1498
1499 BIND(&if_tagged_field);
1500 {
1501 Comment("store tagged field");
1502 HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
1503 value, miss);
1504 }
1505
1506 BIND(&if_double_field);
1507 {
1508 Comment("store double field");
1509 HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
1510 value, miss);
1511 }
1512
1513 BIND(&if_heap_object_field);
1514 {
1515 Comment("store heap object field");
1516 HandleStoreFieldAndReturn(handler_word, holder,
1517 Representation::HeapObject(), value, miss);
1518 }
1519
1520 BIND(&if_smi_field);
1521 {
1522 Comment("store smi field");
1523 HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
1524 value, miss);
1525 }
1526 }
1527
HandleStoreFieldAndReturn(Node * handler_word,Node * holder,Representation representation,Node * value,Label * miss)1528 void AccessorAssembler::HandleStoreFieldAndReturn(Node* handler_word,
1529 Node* holder,
1530 Representation representation,
1531 Node* value, Label* miss) {
1532 Node* prepared_value =
1533 PrepareValueForStore(handler_word, holder, representation, value, miss);
1534
1535 Label if_inobject(this), if_out_of_object(this);
1536 Branch(IsSetWord<StoreHandler::IsInobjectBits>(handler_word), &if_inobject,
1537 &if_out_of_object);
1538
1539 BIND(&if_inobject);
1540 {
1541 StoreNamedField(handler_word, holder, true, representation, prepared_value,
1542 miss);
1543 Return(value);
1544 }
1545
1546 BIND(&if_out_of_object);
1547 {
1548 StoreNamedField(handler_word, holder, false, representation, prepared_value,
1549 miss);
1550 Return(value);
1551 }
1552 }
1553
PrepareValueForStore(Node * handler_word,Node * holder,Representation representation,Node * value,Label * bailout)1554 Node* AccessorAssembler::PrepareValueForStore(Node* handler_word, Node* holder,
1555 Representation representation,
1556 Node* value, Label* bailout) {
1557 if (representation.IsDouble()) {
1558 value = TryTaggedToFloat64(value, bailout);
1559
1560 } else if (representation.IsHeapObject()) {
1561 GotoIf(TaggedIsSmi(value), bailout);
1562
1563 Label done(this);
1564 if (FLAG_track_constant_fields) {
1565 // Skip field type check in favor of constant value check when storing
1566 // to constant field.
1567 GotoIf(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
1568 IntPtrConstant(StoreHandler::kConstField)),
1569 &done);
1570 }
1571 Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word);
1572 TNode<MaybeObject> maybe_field_type =
1573 LoadDescriptorValueOrFieldType(LoadMap(holder), descriptor);
1574
1575 GotoIf(TaggedIsSmi(maybe_field_type), &done);
1576 // Check that value type matches the field type.
1577 {
1578 Node* field_type = ToWeakHeapObject(maybe_field_type, bailout);
1579 Branch(WordEqual(LoadMap(value), field_type), &done, bailout);
1580 }
1581 BIND(&done);
1582
1583 } else if (representation.IsSmi()) {
1584 GotoIfNot(TaggedIsSmi(value), bailout);
1585
1586 } else {
1587 DCHECK(representation.IsTagged());
1588 }
1589 return value;
1590 }
1591
ExtendPropertiesBackingStore(Node * object,Node * index)1592 Node* AccessorAssembler::ExtendPropertiesBackingStore(Node* object,
1593 Node* index) {
1594 Comment("[ Extend storage");
1595
1596 ParameterMode mode = OptimalParameterMode();
1597
1598 // TODO(gsathya): Clean up the type conversions by creating smarter
1599 // helpers that do the correct op based on the mode.
1600 VARIABLE(var_properties, MachineRepresentation::kTaggedPointer);
1601 VARIABLE(var_encoded_hash, MachineRepresentation::kWord32);
1602 VARIABLE(var_length, ParameterRepresentation(mode));
1603
1604 Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset);
1605 var_properties.Bind(properties);
1606
1607 Label if_smi_hash(this), if_property_array(this), extend_store(this);
1608 Branch(TaggedIsSmi(properties), &if_smi_hash, &if_property_array);
1609
1610 BIND(&if_smi_hash);
1611 {
1612 Node* hash = SmiToInt32(properties);
1613 Node* encoded_hash =
1614 Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift));
1615 var_encoded_hash.Bind(encoded_hash);
1616 var_length.Bind(IntPtrOrSmiConstant(0, mode));
1617 var_properties.Bind(EmptyFixedArrayConstant());
1618 Goto(&extend_store);
1619 }
1620
1621 BIND(&if_property_array);
1622 {
1623 Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField(
1624 var_properties.value(), PropertyArray::kLengthAndHashOffset);
1625 var_encoded_hash.Bind(Word32And(
1626 length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask)));
1627 Node* length_intptr = ChangeInt32ToIntPtr(
1628 Word32And(length_and_hash_int32,
1629 Int32Constant(PropertyArray::LengthField::kMask)));
1630 Node* length = IntPtrToParameter(length_intptr, mode);
1631 var_length.Bind(length);
1632 Goto(&extend_store);
1633 }
1634
1635 BIND(&extend_store);
1636 {
1637 VARIABLE(var_new_properties, MachineRepresentation::kTaggedPointer,
1638 var_properties.value());
1639 Label done(this);
1640 // Previous property deletion could have left behind unused backing store
1641 // capacity even for a map that think it doesn't have any unused fields.
1642 // Perform a bounds check to see if we actually have to grow the array.
1643 GotoIf(UintPtrLessThan(index, ParameterToIntPtr(var_length.value(), mode)),
1644 &done);
1645
1646 Node* delta = IntPtrOrSmiConstant(JSObject::kFieldsAdded, mode);
1647 Node* new_capacity = IntPtrOrSmiAdd(var_length.value(), delta, mode);
1648
1649 // Grow properties array.
1650 DCHECK(kMaxNumberOfDescriptors + JSObject::kFieldsAdded <
1651 FixedArrayBase::GetMaxLengthForNewSpaceAllocation(PACKED_ELEMENTS));
1652 // The size of a new properties backing store is guaranteed to be small
1653 // enough that the new backing store will be allocated in new space.
1654 CSA_ASSERT(this,
1655 UintPtrOrSmiLessThan(
1656 new_capacity,
1657 IntPtrOrSmiConstant(
1658 kMaxNumberOfDescriptors + JSObject::kFieldsAdded, mode),
1659 mode));
1660
1661 Node* new_properties = AllocatePropertyArray(new_capacity, mode);
1662 var_new_properties.Bind(new_properties);
1663
1664 FillPropertyArrayWithUndefined(new_properties, var_length.value(),
1665 new_capacity, mode);
1666
1667 // |new_properties| is guaranteed to be in new space, so we can skip
1668 // the write barrier.
1669 CopyPropertyArrayValues(var_properties.value(), new_properties,
1670 var_length.value(), SKIP_WRITE_BARRIER, mode);
1671
1672 // TODO(gsathya): Clean up the type conversions by creating smarter
1673 // helpers that do the correct op based on the mode.
1674 Node* new_capacity_int32 =
1675 TruncateIntPtrToInt32(ParameterToIntPtr(new_capacity, mode));
1676 Node* new_length_and_hash_int32 =
1677 Word32Or(var_encoded_hash.value(), new_capacity_int32);
1678 StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset,
1679 SmiFromInt32(new_length_and_hash_int32));
1680 StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties);
1681 Comment("] Extend storage");
1682 Goto(&done);
1683 BIND(&done);
1684 return var_new_properties.value();
1685 }
1686 }
1687
StoreNamedField(Node * handler_word,Node * object,bool is_inobject,Representation representation,Node * value,Label * bailout)1688 void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object,
1689 bool is_inobject,
1690 Representation representation,
1691 Node* value, Label* bailout) {
1692 bool store_value_as_double = representation.IsDouble();
1693 Node* property_storage = object;
1694 if (!is_inobject) {
1695 property_storage = LoadFastProperties(object);
1696 }
1697
1698 Node* index = DecodeWord<StoreHandler::FieldIndexBits>(handler_word);
1699 Node* offset = IntPtrMul(index, IntPtrConstant(kPointerSize));
1700 if (representation.IsDouble()) {
1701 if (!FLAG_unbox_double_fields || !is_inobject) {
1702 // Load the mutable heap number.
1703 property_storage = LoadObjectField(property_storage, offset);
1704 // Store the double value into it.
1705 offset = IntPtrConstant(HeapNumber::kValueOffset);
1706 }
1707 }
1708
1709 // Do constant value check if necessary.
1710 if (FLAG_track_constant_fields) {
1711 Label done(this);
1712 GotoIfNot(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word),
1713 IntPtrConstant(StoreHandler::kConstField)),
1714 &done);
1715 {
1716 if (store_value_as_double) {
1717 Node* current_value =
1718 LoadObjectField(property_storage, offset, MachineType::Float64());
1719 GotoIfNot(Float64Equal(current_value, value), bailout);
1720 } else {
1721 Node* current_value = LoadObjectField(property_storage, offset);
1722 GotoIfNot(WordEqual(current_value, value), bailout);
1723 }
1724 Goto(&done);
1725 }
1726 BIND(&done);
1727 }
1728
1729 // Do the store.
1730 if (store_value_as_double) {
1731 StoreObjectFieldNoWriteBarrier(property_storage, offset, value,
1732 MachineRepresentation::kFloat64);
1733 } else if (representation.IsSmi()) {
1734 StoreObjectFieldNoWriteBarrier(property_storage, offset, value);
1735 } else {
1736 StoreObjectField(property_storage, offset, value);
1737 }
1738 }
1739
EmitFastElementsBoundsCheck(Node * object,Node * elements,Node * intptr_index,Node * is_jsarray_condition,Label * miss)1740 void AccessorAssembler::EmitFastElementsBoundsCheck(Node* object,
1741 Node* elements,
1742 Node* intptr_index,
1743 Node* is_jsarray_condition,
1744 Label* miss) {
1745 VARIABLE(var_length, MachineType::PointerRepresentation());
1746 Comment("Fast elements bounds check");
1747 Label if_array(this), length_loaded(this, &var_length);
1748 GotoIf(is_jsarray_condition, &if_array);
1749 {
1750 var_length.Bind(SmiUntag(LoadFixedArrayBaseLength(elements)));
1751 Goto(&length_loaded);
1752 }
1753 BIND(&if_array);
1754 {
1755 var_length.Bind(SmiUntag(LoadFastJSArrayLength(object)));
1756 Goto(&length_loaded);
1757 }
1758 BIND(&length_loaded);
1759 GotoIfNot(UintPtrLessThan(intptr_index, var_length.value()), miss);
1760 }
1761
EmitElementLoad(Node * object,Node * elements,Node * elements_kind,SloppyTNode<IntPtrT> intptr_index,Node * is_jsarray_condition,Label * if_hole,Label * rebox_double,Variable * var_double_value,Label * unimplemented_elements_kind,Label * out_of_bounds,Label * miss,ExitPoint * exit_point)1762 void AccessorAssembler::EmitElementLoad(
1763 Node* object, Node* elements, Node* elements_kind,
1764 SloppyTNode<IntPtrT> intptr_index, Node* is_jsarray_condition,
1765 Label* if_hole, Label* rebox_double, Variable* var_double_value,
1766 Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss,
1767 ExitPoint* exit_point) {
1768 Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this),
1769 if_fast_double(this), if_fast_holey_double(this), if_nonfast(this),
1770 if_dictionary(this);
1771 GotoIf(
1772 Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
1773 &if_nonfast);
1774
1775 EmitFastElementsBoundsCheck(object, elements, intptr_index,
1776 is_jsarray_condition, out_of_bounds);
1777 int32_t kinds[] = {// Handled by if_fast_packed.
1778 PACKED_SMI_ELEMENTS, PACKED_ELEMENTS,
1779 // Handled by if_fast_holey.
1780 HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS,
1781 // Handled by if_fast_double.
1782 PACKED_DOUBLE_ELEMENTS,
1783 // Handled by if_fast_holey_double.
1784 HOLEY_DOUBLE_ELEMENTS};
1785 Label* labels[] = {// FAST_{SMI,}_ELEMENTS
1786 &if_fast_packed, &if_fast_packed,
1787 // FAST_HOLEY_{SMI,}_ELEMENTS
1788 &if_fast_holey, &if_fast_holey,
1789 // PACKED_DOUBLE_ELEMENTS
1790 &if_fast_double,
1791 // HOLEY_DOUBLE_ELEMENTS
1792 &if_fast_holey_double};
1793 Switch(elements_kind, unimplemented_elements_kind, kinds, labels,
1794 arraysize(kinds));
1795
1796 BIND(&if_fast_packed);
1797 {
1798 Comment("fast packed elements");
1799 exit_point->Return(LoadFixedArrayElement(CAST(elements), intptr_index));
1800 }
1801
1802 BIND(&if_fast_holey);
1803 {
1804 Comment("fast holey elements");
1805 Node* element = LoadFixedArrayElement(CAST(elements), intptr_index);
1806 GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
1807 exit_point->Return(element);
1808 }
1809
1810 BIND(&if_fast_double);
1811 {
1812 Comment("packed double elements");
1813 var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index,
1814 MachineType::Float64()));
1815 Goto(rebox_double);
1816 }
1817
1818 BIND(&if_fast_holey_double);
1819 {
1820 Comment("holey double elements");
1821 Node* value = LoadFixedDoubleArrayElement(elements, intptr_index,
1822 MachineType::Float64(), 0,
1823 INTPTR_PARAMETERS, if_hole);
1824 var_double_value->Bind(value);
1825 Goto(rebox_double);
1826 }
1827
1828 BIND(&if_nonfast);
1829 {
1830 STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
1831 GotoIf(Int32GreaterThanOrEqual(
1832 elements_kind,
1833 Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
1834 &if_typed_array);
1835 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
1836 &if_dictionary);
1837 Goto(unimplemented_elements_kind);
1838 }
1839
1840 BIND(&if_dictionary);
1841 {
1842 Comment("dictionary elements");
1843 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
1844
1845 TNode<Object> value = BasicLoadNumberDictionaryElement(
1846 CAST(elements), intptr_index, miss, if_hole);
1847 exit_point->Return(value);
1848 }
1849
1850 BIND(&if_typed_array);
1851 {
1852 Comment("typed elements");
1853 // Check if buffer has been neutered.
1854 Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
1855 GotoIf(IsDetachedBuffer(buffer), miss);
1856
1857 // Bounds check.
1858 Node* length = SmiUntag(LoadTypedArrayLength(CAST(object)));
1859 GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds);
1860
1861 Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements));
1862
1863 Label uint8_elements(this), int8_elements(this), uint16_elements(this),
1864 int16_elements(this), uint32_elements(this), int32_elements(this),
1865 float32_elements(this), float64_elements(this), bigint64_elements(this),
1866 biguint64_elements(this);
1867 Label* elements_kind_labels[] = {
1868 &uint8_elements, &uint8_elements, &int8_elements,
1869 &uint16_elements, &int16_elements, &uint32_elements,
1870 &int32_elements, &float32_elements, &float64_elements,
1871 &bigint64_elements, &biguint64_elements};
1872 int32_t elements_kinds[] = {
1873 UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
1874 UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
1875 INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS,
1876 BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS};
1877 const size_t kTypedElementsKindCount =
1878 LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
1879 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
1880 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
1881 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
1882 Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
1883 kTypedElementsKindCount);
1884 BIND(&uint8_elements);
1885 {
1886 Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
1887 Node* element = Load(MachineType::Uint8(), backing_store, intptr_index);
1888 exit_point->Return(SmiFromInt32(element));
1889 }
1890 BIND(&int8_elements);
1891 {
1892 Comment("INT8_ELEMENTS");
1893 Node* element = Load(MachineType::Int8(), backing_store, intptr_index);
1894 exit_point->Return(SmiFromInt32(element));
1895 }
1896 BIND(&uint16_elements);
1897 {
1898 Comment("UINT16_ELEMENTS");
1899 Node* index = WordShl(intptr_index, IntPtrConstant(1));
1900 Node* element = Load(MachineType::Uint16(), backing_store, index);
1901 exit_point->Return(SmiFromInt32(element));
1902 }
1903 BIND(&int16_elements);
1904 {
1905 Comment("INT16_ELEMENTS");
1906 Node* index = WordShl(intptr_index, IntPtrConstant(1));
1907 Node* element = Load(MachineType::Int16(), backing_store, index);
1908 exit_point->Return(SmiFromInt32(element));
1909 }
1910 BIND(&uint32_elements);
1911 {
1912 Comment("UINT32_ELEMENTS");
1913 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1914 Node* element = Load(MachineType::Uint32(), backing_store, index);
1915 exit_point->Return(ChangeUint32ToTagged(element));
1916 }
1917 BIND(&int32_elements);
1918 {
1919 Comment("INT32_ELEMENTS");
1920 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1921 Node* element = Load(MachineType::Int32(), backing_store, index);
1922 exit_point->Return(ChangeInt32ToTagged(element));
1923 }
1924 BIND(&float32_elements);
1925 {
1926 Comment("FLOAT32_ELEMENTS");
1927 Node* index = WordShl(intptr_index, IntPtrConstant(2));
1928 Node* element = Load(MachineType::Float32(), backing_store, index);
1929 var_double_value->Bind(ChangeFloat32ToFloat64(element));
1930 Goto(rebox_double);
1931 }
1932 BIND(&float64_elements);
1933 {
1934 Comment("FLOAT64_ELEMENTS");
1935 Node* index = WordShl(intptr_index, IntPtrConstant(3));
1936 Node* element = Load(MachineType::Float64(), backing_store, index);
1937 var_double_value->Bind(element);
1938 Goto(rebox_double);
1939 }
1940 BIND(&bigint64_elements);
1941 {
1942 Comment("BIGINT64_ELEMENTS");
1943 exit_point->Return(LoadFixedTypedArrayElementAsTagged(
1944 backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS));
1945 }
1946 BIND(&biguint64_elements);
1947 {
1948 Comment("BIGUINT64_ELEMENTS");
1949 exit_point->Return(LoadFixedTypedArrayElementAsTagged(
1950 backing_store, intptr_index, BIGUINT64_ELEMENTS, INTPTR_PARAMETERS));
1951 }
1952 }
1953 }
1954
NameDictionaryNegativeLookup(Node * object,SloppyTNode<Name> name,Label * miss)1955 void AccessorAssembler::NameDictionaryNegativeLookup(Node* object,
1956 SloppyTNode<Name> name,
1957 Label* miss) {
1958 CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
1959 TNode<NameDictionary> properties = CAST(LoadSlowProperties(object));
1960 // Ensure the property does not exist in a dictionary-mode object.
1961 TVARIABLE(IntPtrT, var_name_index);
1962 Label done(this);
1963 NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index,
1964 &done);
1965 BIND(&done);
1966 }
1967
BranchIfStrictMode(Node * vector,Node * slot,Label * if_strict)1968 void AccessorAssembler::BranchIfStrictMode(Node* vector, Node* slot,
1969 Label* if_strict) {
1970 Node* sfi =
1971 LoadObjectField(vector, FeedbackVector::kSharedFunctionInfoOffset);
1972 TNode<FeedbackMetadata> metadata = CAST(LoadObjectField(
1973 sfi, SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset));
1974 Node* slot_int = SmiToInt32(slot);
1975
1976 // See VectorICComputer::index().
1977 const int kItemsPerWord = FeedbackMetadata::VectorICComputer::kItemsPerWord;
1978 Node* word_index = Int32Div(slot_int, Int32Constant(kItemsPerWord));
1979 Node* word_offset = Int32Mod(slot_int, Int32Constant(kItemsPerWord));
1980
1981 int32_t first_item = FeedbackMetadata::kHeaderSize - kHeapObjectTag;
1982 Node* offset =
1983 ElementOffsetFromIndex(ChangeInt32ToIntPtr(word_index), UINT32_ELEMENTS,
1984 INTPTR_PARAMETERS, first_item);
1985
1986 Node* data = Load(MachineType::Int32(), metadata, offset);
1987
1988 // See VectorICComputer::decode().
1989 const int kBitsPerItem = FeedbackMetadata::kFeedbackSlotKindBits;
1990 Node* shift = Int32Mul(word_offset, Int32Constant(kBitsPerItem));
1991 const int kMask = FeedbackMetadata::VectorICComputer::kMask;
1992 Node* kind = Word32And(Word32Shr(data, shift), Int32Constant(kMask));
1993
1994 STATIC_ASSERT(FeedbackSlotKind::kStoreGlobalSloppy <=
1995 FeedbackSlotKind::kLastSloppyKind);
1996 STATIC_ASSERT(FeedbackSlotKind::kStoreKeyedSloppy <=
1997 FeedbackSlotKind::kLastSloppyKind);
1998 STATIC_ASSERT(FeedbackSlotKind::kStoreNamedSloppy <=
1999 FeedbackSlotKind::kLastSloppyKind);
2000 GotoIfNot(Int32LessThanOrEqual(kind, Int32Constant(static_cast<int>(
2001 FeedbackSlotKind::kLastSloppyKind))),
2002 if_strict);
2003 }
2004
InvalidateValidityCellIfPrototype(Node * map,Node * bitfield2)2005 void AccessorAssembler::InvalidateValidityCellIfPrototype(Node* map,
2006 Node* bitfield2) {
2007 Label is_prototype(this), cont(this);
2008 if (bitfield2 == nullptr) {
2009 bitfield2 = LoadMapBitField2(map);
2010 }
2011
2012 Branch(IsSetWord32(bitfield2, Map::IsPrototypeMapBit::kMask), &is_prototype,
2013 &cont);
2014
2015 BIND(&is_prototype);
2016 {
2017 Node* maybe_prototype_info =
2018 LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
2019 // If there's no prototype info then there's nothing to invalidate.
2020 GotoIf(TaggedIsSmi(maybe_prototype_info), &cont);
2021
2022 Node* function = ExternalConstant(
2023 ExternalReference::invalidate_prototype_chains_function());
2024 CallCFunction1(MachineType::AnyTagged(), MachineType::AnyTagged(), function,
2025 map);
2026 Goto(&cont);
2027 }
2028 BIND(&cont);
2029 }
2030
GenericElementLoad(Node * receiver,Node * receiver_map,SloppyTNode<Int32T> instance_type,Node * index,Label * slow)2031 void AccessorAssembler::GenericElementLoad(Node* receiver, Node* receiver_map,
2032 SloppyTNode<Int32T> instance_type,
2033 Node* index, Label* slow) {
2034 Comment("integer index");
2035
2036 ExitPoint direct_exit(this);
2037
2038 Label if_custom(this), if_element_hole(this), if_oob(this);
2039 // Receivers requiring non-standard element accesses (interceptors, access
2040 // checks, strings and string wrappers, proxies) are handled in the runtime.
2041 GotoIf(IsCustomElementsReceiverInstanceType(instance_type), &if_custom);
2042 Node* elements = LoadElements(receiver);
2043 Node* elements_kind = LoadMapElementsKind(receiver_map);
2044 Node* is_jsarray_condition = InstanceTypeEqual(instance_type, JS_ARRAY_TYPE);
2045 VARIABLE(var_double_value, MachineRepresentation::kFloat64);
2046 Label rebox_double(this, &var_double_value);
2047
2048 // Unimplemented elements kinds fall back to a runtime call.
2049 Label* unimplemented_elements_kind = slow;
2050 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
2051 EmitElementLoad(receiver, elements, elements_kind, index,
2052 is_jsarray_condition, &if_element_hole, &rebox_double,
2053 &var_double_value, unimplemented_elements_kind, &if_oob, slow,
2054 &direct_exit);
2055
2056 BIND(&rebox_double);
2057 Return(AllocateHeapNumberWithValue(var_double_value.value()));
2058
2059 BIND(&if_oob);
2060 {
2061 Comment("out of bounds");
2062 // Positive OOB indices are effectively the same as hole loads.
2063 GotoIf(IntPtrGreaterThanOrEqual(index, IntPtrConstant(0)),
2064 &if_element_hole);
2065 // Negative keys can't take the fast OOB path, except for typed arrays.
2066 GotoIfNot(InstanceTypeEqual(instance_type, JS_TYPED_ARRAY_TYPE), slow);
2067 Return(UndefinedConstant());
2068 }
2069
2070 BIND(&if_element_hole);
2071 {
2072 Comment("found the hole");
2073 Label return_undefined(this);
2074 BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, slow);
2075
2076 BIND(&return_undefined);
2077 Return(UndefinedConstant());
2078 }
2079
2080 BIND(&if_custom);
2081 {
2082 Comment("check if string");
2083 GotoIfNot(IsStringInstanceType(instance_type), slow);
2084 Comment("load string character");
2085 Node* length = LoadAndUntagObjectField(receiver, String::kLengthOffset);
2086 GotoIfNot(UintPtrLessThan(index, length), slow);
2087 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
2088 TailCallBuiltin(Builtins::kStringCharAt, NoContextConstant(), receiver,
2089 index);
2090 }
2091 }
2092
GenericPropertyLoad(Node * receiver,Node * receiver_map,SloppyTNode<Int32T> instance_type,const LoadICParameters * p,Label * slow,UseStubCache use_stub_cache)2093 void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
2094 SloppyTNode<Int32T> instance_type,
2095 const LoadICParameters* p,
2096 Label* slow,
2097 UseStubCache use_stub_cache) {
2098 ExitPoint direct_exit(this);
2099
2100 Comment("key is unique name");
2101 Label if_found_on_receiver(this), if_property_dictionary(this),
2102 lookup_prototype_chain(this), special_receiver(this);
2103 VARIABLE(var_details, MachineRepresentation::kWord32);
2104 VARIABLE(var_value, MachineRepresentation::kTagged);
2105
2106 // Receivers requiring non-standard accesses (interceptors, access
2107 // checks, strings and string wrappers) are handled in the runtime.
2108 GotoIf(IsSpecialReceiverInstanceType(instance_type), &special_receiver);
2109
2110 // Check if the receiver has fast or slow properties.
2111 Node* bitfield3 = LoadMapBitField3(receiver_map);
2112 GotoIf(IsSetWord32<Map::IsDictionaryMapBit>(bitfield3),
2113 &if_property_dictionary);
2114
2115 // Try looking up the property on the receiver; if unsuccessful, look
2116 // for a handler in the stub cache.
2117 TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map);
2118
2119 Label if_descriptor_found(this), stub_cache(this);
2120 TVARIABLE(IntPtrT, var_name_index);
2121 Label* notfound =
2122 use_stub_cache == kUseStubCache ? &stub_cache : &lookup_prototype_chain;
2123 DescriptorLookup(p->name, descriptors, bitfield3, &if_descriptor_found,
2124 &var_name_index, notfound);
2125
2126 BIND(&if_descriptor_found);
2127 {
2128 LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
2129 var_name_index.value(), &var_details,
2130 &var_value);
2131 Goto(&if_found_on_receiver);
2132 }
2133
2134 if (use_stub_cache == kUseStubCache) {
2135 BIND(&stub_cache);
2136 Comment("stub cache probe for fast property load");
2137 TVARIABLE(MaybeObject, var_handler);
2138 Label found_handler(this, &var_handler), stub_cache_miss(this);
2139 TryProbeStubCache(isolate()->load_stub_cache(), receiver, p->name,
2140 &found_handler, &var_handler, &stub_cache_miss);
2141 BIND(&found_handler);
2142 {
2143 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &stub_cache_miss,
2144 &direct_exit);
2145 }
2146
2147 BIND(&stub_cache_miss);
2148 {
2149 // TODO(jkummerow): Check if the property exists on the prototype
2150 // chain. If it doesn't, then there's no point in missing.
2151 Comment("KeyedLoadGeneric_miss");
2152 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
2153 p->name, p->slot, p->vector);
2154 }
2155 }
2156
2157 BIND(&if_property_dictionary);
2158 {
2159 Comment("dictionary property load");
2160 // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
2161 // seeing global objects here (which would need special handling).
2162
2163 TVARIABLE(IntPtrT, var_name_index);
2164 Label dictionary_found(this, &var_name_index);
2165 TNode<NameDictionary> properties = CAST(LoadSlowProperties(receiver));
2166 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name),
2167 &dictionary_found, &var_name_index,
2168 &lookup_prototype_chain);
2169 BIND(&dictionary_found);
2170 {
2171 LoadPropertyFromNameDictionary(properties, var_name_index.value(),
2172 &var_details, &var_value);
2173 Goto(&if_found_on_receiver);
2174 }
2175 }
2176
2177 BIND(&if_found_on_receiver);
2178 {
2179 Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
2180 p->context, receiver, slow);
2181 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1);
2182 Return(value);
2183 }
2184
2185 BIND(&lookup_prototype_chain);
2186 {
2187 VARIABLE(var_holder_map, MachineRepresentation::kTagged);
2188 VARIABLE(var_holder_instance_type, MachineRepresentation::kWord32);
2189 Label return_undefined(this);
2190 Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type};
2191 Label loop(this, arraysize(merged_variables), merged_variables);
2192
2193 var_holder_map.Bind(receiver_map);
2194 var_holder_instance_type.Bind(instance_type);
2195 // Private symbols must not be looked up on the prototype chain.
2196 GotoIf(IsPrivateSymbol(p->name), &return_undefined);
2197 Goto(&loop);
2198 BIND(&loop);
2199 {
2200 // Bailout if it can be an integer indexed exotic case.
2201 GotoIf(InstanceTypeEqual(var_holder_instance_type.value(),
2202 JS_TYPED_ARRAY_TYPE),
2203 slow);
2204 Node* proto = LoadMapPrototype(var_holder_map.value());
2205 GotoIf(WordEqual(proto, NullConstant()), &return_undefined);
2206 Node* proto_map = LoadMap(proto);
2207 Node* proto_instance_type = LoadMapInstanceType(proto_map);
2208 var_holder_map.Bind(proto_map);
2209 var_holder_instance_type.Bind(proto_instance_type);
2210 Label next_proto(this), return_value(this, &var_value), goto_slow(this);
2211 TryGetOwnProperty(p->context, receiver, proto, proto_map,
2212 proto_instance_type, p->name, &return_value, &var_value,
2213 &next_proto, &goto_slow);
2214
2215 // This trampoline and the next are required to appease Turbofan's
2216 // variable merging.
2217 BIND(&next_proto);
2218 Goto(&loop);
2219
2220 BIND(&goto_slow);
2221 Goto(slow);
2222
2223 BIND(&return_value);
2224 Return(var_value.value());
2225 }
2226
2227 BIND(&return_undefined);
2228 Return(UndefinedConstant());
2229 }
2230
2231 BIND(&special_receiver);
2232 {
2233 // TODO(jkummerow): Consider supporting JSModuleNamespace.
2234 GotoIfNot(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), slow);
2235
2236 // Private field/symbol lookup is not supported.
2237 GotoIf(IsPrivateSymbol(p->name), slow);
2238
2239 direct_exit.ReturnCallStub(
2240 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
2241 p->context, receiver /*holder is the same as receiver*/, p->name,
2242 receiver, SmiConstant(OnNonExistent::kReturnUndefined));
2243 }
2244 }
2245
2246 //////////////////// Stub cache access helpers.
2247
2248 enum AccessorAssembler::StubCacheTable : int {
2249 kPrimary = static_cast<int>(StubCache::kPrimary),
2250 kSecondary = static_cast<int>(StubCache::kSecondary)
2251 };
2252
StubCachePrimaryOffset(Node * name,Node * map)2253 Node* AccessorAssembler::StubCachePrimaryOffset(Node* name, Node* map) {
2254 // See v8::internal::StubCache::PrimaryOffset().
2255 STATIC_ASSERT(StubCache::kCacheIndexShift == Name::kHashShift);
2256 // Compute the hash of the name (use entire hash field).
2257 Node* hash_field = LoadNameHashField(name);
2258 CSA_ASSERT(this,
2259 Word32Equal(Word32And(hash_field,
2260 Int32Constant(Name::kHashNotComputedMask)),
2261 Int32Constant(0)));
2262
2263 // Using only the low bits in 64-bit mode is unlikely to increase the
2264 // risk of collision even if the heap is spread over an area larger than
2265 // 4Gb (and not at all if it isn't).
2266 Node* map32 = TruncateIntPtrToInt32(BitcastTaggedToWord(map));
2267 // Base the offset on a simple combination of name and map.
2268 Node* hash = Int32Add(hash_field, map32);
2269 uint32_t mask = (StubCache::kPrimaryTableSize - 1)
2270 << StubCache::kCacheIndexShift;
2271 return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
2272 }
2273
StubCacheSecondaryOffset(Node * name,Node * seed)2274 Node* AccessorAssembler::StubCacheSecondaryOffset(Node* name, Node* seed) {
2275 // See v8::internal::StubCache::SecondaryOffset().
2276
2277 // Use the seed from the primary cache in the secondary cache.
2278 Node* name32 = TruncateIntPtrToInt32(BitcastTaggedToWord(name));
2279 Node* hash = Int32Sub(TruncateIntPtrToInt32(seed), name32);
2280 hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
2281 int32_t mask = (StubCache::kSecondaryTableSize - 1)
2282 << StubCache::kCacheIndexShift;
2283 return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
2284 }
2285
TryProbeStubCacheTable(StubCache * stub_cache,StubCacheTable table_id,Node * entry_offset,Node * name,Node * map,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)2286 void AccessorAssembler::TryProbeStubCacheTable(
2287 StubCache* stub_cache, StubCacheTable table_id, Node* entry_offset,
2288 Node* name, Node* map, Label* if_handler,
2289 TVariable<MaybeObject>* var_handler, Label* if_miss) {
2290 StubCache::Table table = static_cast<StubCache::Table>(table_id);
2291 #ifdef DEBUG
2292 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
2293 Goto(if_miss);
2294 return;
2295 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) {
2296 Goto(if_miss);
2297 return;
2298 }
2299 #endif
2300 // The {table_offset} holds the entry offset times four (due to masking
2301 // and shifting optimizations).
2302 const int kMultiplier = sizeof(StubCache::Entry) >> Name::kHashShift;
2303 entry_offset = IntPtrMul(entry_offset, IntPtrConstant(kMultiplier));
2304
2305 // Check that the key in the entry matches the name.
2306 Node* key_base = ExternalConstant(
2307 ExternalReference::Create(stub_cache->key_reference(table)));
2308 Node* entry_key = Load(MachineType::Pointer(), key_base, entry_offset);
2309 GotoIf(WordNotEqual(name, entry_key), if_miss);
2310
2311 // Get the map entry from the cache.
2312 DCHECK_EQ(kPointerSize * 2, stub_cache->map_reference(table).address() -
2313 stub_cache->key_reference(table).address());
2314 Node* entry_map =
2315 Load(MachineType::Pointer(), key_base,
2316 IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize * 2)));
2317 GotoIf(WordNotEqual(map, entry_map), if_miss);
2318
2319 DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() -
2320 stub_cache->key_reference(table).address());
2321 TNode<MaybeObject> handler = ReinterpretCast<MaybeObject>(
2322 Load(MachineType::TaggedPointer(), key_base,
2323 IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize))));
2324
2325 // We found the handler.
2326 *var_handler = handler;
2327 Goto(if_handler);
2328 }
2329
TryProbeStubCache(StubCache * stub_cache,Node * receiver,Node * name,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)2330 void AccessorAssembler::TryProbeStubCache(StubCache* stub_cache, Node* receiver,
2331 Node* name, Label* if_handler,
2332 TVariable<MaybeObject>* var_handler,
2333 Label* if_miss) {
2334 Label try_secondary(this), miss(this);
2335
2336 Counters* counters = isolate()->counters();
2337 IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
2338
2339 // Check that the {receiver} isn't a smi.
2340 GotoIf(TaggedIsSmi(receiver), &miss);
2341
2342 Node* receiver_map = LoadMap(receiver);
2343
2344 // Probe the primary table.
2345 Node* primary_offset = StubCachePrimaryOffset(name, receiver_map);
2346 TryProbeStubCacheTable(stub_cache, kPrimary, primary_offset, name,
2347 receiver_map, if_handler, var_handler, &try_secondary);
2348
2349 BIND(&try_secondary);
2350 {
2351 // Probe the secondary table.
2352 Node* secondary_offset = StubCacheSecondaryOffset(name, primary_offset);
2353 TryProbeStubCacheTable(stub_cache, kSecondary, secondary_offset, name,
2354 receiver_map, if_handler, var_handler, &miss);
2355 }
2356
2357 BIND(&miss);
2358 {
2359 IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
2360 Goto(if_miss);
2361 }
2362 }
2363
2364 //////////////////// Entry points into private implementation (one per stub).
2365
LoadIC_BytecodeHandler(const LoadICParameters * p,ExitPoint * exit_point)2366 void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p,
2367 ExitPoint* exit_point) {
2368 // Must be kept in sync with LoadIC.
2369
2370 // This function is hand-tuned to omit frame construction for common cases,
2371 // e.g.: monomorphic field and constant loads through smi handlers.
2372 // Polymorphic ICs with a hit in the first two entries also omit frames.
2373 // TODO(jgruber): Frame omission is fragile and can be affected by minor
2374 // changes in control flow and logic. We currently have no way of ensuring
2375 // that no frame is constructed, so it's easy to break this optimization by
2376 // accident.
2377 Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred);
2378
2379 // Inlined fast path.
2380 {
2381 Comment("LoadIC_BytecodeHandler_fast");
2382
2383 Node* recv_map = LoadReceiverMap(p->receiver);
2384 GotoIf(IsDeprecatedMap(recv_map), &miss);
2385
2386 TVARIABLE(MaybeObject, var_handler);
2387 Label try_polymorphic(this), if_handler(this, &var_handler);
2388
2389 TNode<MaybeObject> feedback =
2390 TryMonomorphicCase(p->slot, p->vector, recv_map, &if_handler,
2391 &var_handler, &try_polymorphic);
2392
2393 BIND(&if_handler);
2394 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, exit_point);
2395
2396 BIND(&try_polymorphic);
2397 {
2398 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
2399 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &stub_call);
2400 HandlePolymorphicCase(recv_map, CAST(strong_feedback), &if_handler,
2401 &var_handler, &miss, 2);
2402 }
2403 }
2404
2405 BIND(&stub_call);
2406 {
2407 Comment("LoadIC_BytecodeHandler_noninlined");
2408
2409 // Call into the stub that implements the non-inlined parts of LoadIC.
2410 Callable ic =
2411 Builtins::CallableFor(isolate(), Builtins::kLoadIC_Noninlined);
2412 Node* code_target = HeapConstant(ic.code());
2413 exit_point->ReturnCallStub(ic.descriptor(), code_target, p->context,
2414 p->receiver, p->name, p->slot, p->vector);
2415 }
2416
2417 BIND(&miss);
2418 {
2419 Comment("LoadIC_BytecodeHandler_miss");
2420
2421 exit_point->ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context,
2422 p->receiver, p->name, p->slot, p->vector);
2423 }
2424 }
2425
LoadIC(const LoadICParameters * p)2426 void AccessorAssembler::LoadIC(const LoadICParameters* p) {
2427 // Must be kept in sync with LoadIC_BytecodeHandler.
2428
2429 ExitPoint direct_exit(this);
2430
2431 TVARIABLE(MaybeObject, var_handler);
2432 Label if_handler(this, &var_handler), non_inlined(this, Label::kDeferred),
2433 try_polymorphic(this), miss(this, Label::kDeferred);
2434
2435 Node* receiver_map = LoadReceiverMap(p->receiver);
2436 GotoIf(IsDeprecatedMap(receiver_map), &miss);
2437
2438 // Check monomorphic case.
2439 TNode<MaybeObject> feedback =
2440 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2441 &var_handler, &try_polymorphic);
2442 BIND(&if_handler);
2443 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit);
2444
2445 BIND(&try_polymorphic);
2446 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
2447 {
2448 // Check polymorphic case.
2449 Comment("LoadIC_try_polymorphic");
2450 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined);
2451 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
2452 &var_handler, &miss, 2);
2453 }
2454
2455 BIND(&non_inlined);
2456 {
2457 LoadIC_Noninlined(p, receiver_map, strong_feedback, &var_handler,
2458 &if_handler, &miss, &direct_exit);
2459 }
2460
2461 BIND(&miss);
2462 direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver,
2463 p->name, p->slot, p->vector);
2464 }
2465
LoadIC_Noninlined(const LoadICParameters * p,Node * receiver_map,TNode<HeapObject> feedback,TVariable<MaybeObject> * var_handler,Label * if_handler,Label * miss,ExitPoint * exit_point)2466 void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p,
2467 Node* receiver_map,
2468 TNode<HeapObject> feedback,
2469 TVariable<MaybeObject>* var_handler,
2470 Label* if_handler, Label* miss,
2471 ExitPoint* exit_point) {
2472 Label try_uninitialized(this, Label::kDeferred);
2473
2474 // Neither deprecated map nor monomorphic. These cases are handled in the
2475 // bytecode handler.
2476 CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map)));
2477 CSA_ASSERT(this, WordNotEqual(receiver_map, feedback));
2478 CSA_ASSERT(this, Word32BinaryNot(IsWeakFixedArrayMap(LoadMap(feedback))));
2479 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
2480
2481 {
2482 // Check megamorphic case.
2483 GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2484 &try_uninitialized);
2485
2486 TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name,
2487 if_handler, var_handler, miss);
2488 }
2489
2490 BIND(&try_uninitialized);
2491 {
2492 // Check uninitialized case.
2493 GotoIfNot(
2494 WordEqual(feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2495 miss);
2496 exit_point->ReturnCallStub(
2497 Builtins::CallableFor(isolate(), Builtins::kLoadIC_Uninitialized),
2498 p->context, p->receiver, p->name, p->slot, p->vector);
2499 }
2500 }
2501
LoadIC_Uninitialized(const LoadICParameters * p)2502 void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) {
2503 Label miss(this, Label::kDeferred);
2504 Node* receiver = p->receiver;
2505 GotoIf(TaggedIsSmi(receiver), &miss);
2506 Node* receiver_map = LoadMap(receiver);
2507 Node* instance_type = LoadMapInstanceType(receiver_map);
2508
2509 // Optimistically write the state transition to the vector.
2510 StoreFeedbackVectorSlot(p->vector, p->slot,
2511 LoadRoot(Heap::kpremonomorphic_symbolRootIndex),
2512 SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS);
2513 StoreWeakReferenceInFeedbackVector(p->vector, p->slot, receiver_map,
2514 kPointerSize, SMI_PARAMETERS);
2515
2516 {
2517 // Special case for Function.prototype load, because it's very common
2518 // for ICs that are only executed once (MyFunc.prototype.foo = ...).
2519 Label not_function_prototype(this, Label::kDeferred);
2520 GotoIfNot(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
2521 ¬_function_prototype);
2522 GotoIfNot(IsPrototypeString(p->name), ¬_function_prototype);
2523
2524 GotoIfPrototypeRequiresRuntimeLookup(CAST(receiver), CAST(receiver_map),
2525 ¬_function_prototype);
2526 Return(LoadJSFunctionPrototype(receiver, &miss));
2527 BIND(¬_function_prototype);
2528 }
2529
2530 GenericPropertyLoad(receiver, receiver_map, instance_type, p, &miss,
2531 kDontUseStubCache);
2532
2533 BIND(&miss);
2534 {
2535 // Undo the optimistic state transition.
2536 StoreFeedbackVectorSlot(p->vector, p->slot,
2537 LoadRoot(Heap::kuninitialized_symbolRootIndex),
2538 SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS);
2539
2540 TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
2541 p->slot, p->vector);
2542 }
2543 }
2544
LoadGlobalIC(TNode<FeedbackVector> vector,Node * slot,const LazyNode<Context> & lazy_context,const LazyNode<Name> & lazy_name,TypeofMode typeof_mode,ExitPoint * exit_point,ParameterMode slot_mode)2545 void AccessorAssembler::LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot,
2546 const LazyNode<Context>& lazy_context,
2547 const LazyNode<Name>& lazy_name,
2548 TypeofMode typeof_mode,
2549 ExitPoint* exit_point,
2550 ParameterMode slot_mode) {
2551 Label try_handler(this, Label::kDeferred), miss(this, Label::kDeferred);
2552 LoadGlobalIC_TryPropertyCellCase(vector, slot, lazy_context, exit_point,
2553 &try_handler, &miss, slot_mode);
2554
2555 BIND(&try_handler);
2556 LoadGlobalIC_TryHandlerCase(vector, slot, lazy_context, lazy_name,
2557 typeof_mode, exit_point, &miss, slot_mode);
2558
2559 BIND(&miss);
2560 {
2561 Comment("LoadGlobalIC_MissCase");
2562 TNode<Context> context = lazy_context();
2563 TNode<Name> name = lazy_name();
2564 exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Miss, context, name,
2565 ParameterToTagged(slot, slot_mode), vector);
2566 }
2567 }
2568
LoadGlobalIC_TryPropertyCellCase(TNode<FeedbackVector> vector,Node * slot,const LazyNode<Context> & lazy_context,ExitPoint * exit_point,Label * try_handler,Label * miss,ParameterMode slot_mode)2569 void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase(
2570 TNode<FeedbackVector> vector, Node* slot,
2571 const LazyNode<Context>& lazy_context, ExitPoint* exit_point,
2572 Label* try_handler, Label* miss, ParameterMode slot_mode) {
2573 Comment("LoadGlobalIC_TryPropertyCellCase");
2574
2575 Label if_lexical_var(this), if_property_cell(this);
2576 TNode<MaybeObject> maybe_weak_ref =
2577 LoadFeedbackVectorSlot(vector, slot, 0, slot_mode);
2578 Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell);
2579
2580 BIND(&if_property_cell);
2581 {
2582 // Load value or try handler case if the weak reference is cleared.
2583 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_weak_ref));
2584 TNode<PropertyCell> property_cell =
2585 CAST(ToWeakHeapObject(maybe_weak_ref, try_handler));
2586 TNode<Object> value =
2587 LoadObjectField(property_cell, PropertyCell::kValueOffset);
2588 GotoIf(WordEqual(value, TheHoleConstant()), miss);
2589 exit_point->Return(value);
2590 }
2591
2592 BIND(&if_lexical_var);
2593 {
2594 Comment("Load lexical variable");
2595 TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref));
2596 TNode<IntPtrT> context_index =
2597 Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler));
2598 TNode<IntPtrT> slot_index =
2599 Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler));
2600 TNode<Context> context = lazy_context();
2601 TNode<Context> script_context = LoadScriptContext(context, context_index);
2602 TNode<Object> result = LoadContextElement(script_context, slot_index);
2603 exit_point->Return(result);
2604 }
2605 }
2606
LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector,Node * slot,const LazyNode<Context> & lazy_context,const LazyNode<Name> & lazy_name,TypeofMode typeof_mode,ExitPoint * exit_point,Label * miss,ParameterMode slot_mode)2607 void AccessorAssembler::LoadGlobalIC_TryHandlerCase(
2608 TNode<FeedbackVector> vector, Node* slot,
2609 const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name,
2610 TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss,
2611 ParameterMode slot_mode) {
2612 Comment("LoadGlobalIC_TryHandlerCase");
2613
2614 Label call_handler(this), non_smi(this);
2615
2616 TNode<MaybeObject> feedback_element =
2617 LoadFeedbackVectorSlot(vector, slot, kPointerSize, slot_mode);
2618 TNode<Object> handler = CAST(feedback_element);
2619 GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2620 miss);
2621
2622 OnNonExistent on_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF
2623 ? OnNonExistent::kThrowReferenceError
2624 : OnNonExistent::kReturnUndefined;
2625
2626 TNode<Context> context = lazy_context();
2627 TNode<Context> native_context = LoadNativeContext(context);
2628 TNode<JSGlobalProxy> receiver =
2629 CAST(LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX));
2630 Node* holder = LoadContextElement(native_context, Context::EXTENSION_INDEX);
2631
2632 LoadICParameters p(context, receiver, lazy_name(),
2633 ParameterToTagged(slot, slot_mode), vector, holder);
2634
2635 HandleLoadICHandlerCase(&p, handler, miss, exit_point, ICMode::kGlobalIC,
2636 on_nonexistent);
2637 }
2638
KeyedLoadIC(const LoadICParameters * p)2639 void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) {
2640 ExitPoint direct_exit(this);
2641
2642 TVARIABLE(MaybeObject, var_handler);
2643 Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred),
2644 try_megamorphic(this, Label::kDeferred),
2645 try_polymorphic_name(this, Label::kDeferred),
2646 miss(this, Label::kDeferred);
2647
2648 Node* receiver_map = LoadReceiverMap(p->receiver);
2649 GotoIf(IsDeprecatedMap(receiver_map), &miss);
2650
2651 // Check monomorphic case.
2652 TNode<MaybeObject> feedback =
2653 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2654 &var_handler, &try_polymorphic);
2655 BIND(&if_handler);
2656 {
2657 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit,
2658 ICMode::kNonGlobalIC,
2659 OnNonExistent::kReturnUndefined, kSupportElements);
2660 }
2661
2662 BIND(&try_polymorphic);
2663 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
2664 {
2665 // Check polymorphic case.
2666 Comment("KeyedLoadIC_try_polymorphic");
2667 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
2668 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
2669 &var_handler, &miss, 2);
2670 }
2671
2672 BIND(&try_megamorphic);
2673 {
2674 // Check megamorphic case.
2675 Comment("KeyedLoadIC_try_megamorphic");
2676 GotoIfNot(WordEqual(strong_feedback,
2677 LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2678 &try_polymorphic_name);
2679 // TODO(jkummerow): Inline this? Or some of it?
2680 TailCallBuiltin(Builtins::kKeyedLoadIC_Megamorphic, p->context, p->receiver,
2681 p->name, p->slot, p->vector);
2682 }
2683 BIND(&try_polymorphic_name);
2684 {
2685 // We might have a name in feedback, and a weak fixed array in the next
2686 // slot.
2687 Node* name = p->name;
2688 Comment("KeyedLoadIC_try_polymorphic_name");
2689 VARIABLE(var_name, MachineRepresentation::kTagged, name);
2690 VARIABLE(var_index, MachineType::PointerRepresentation());
2691 Label if_polymorphic_name(this, &var_name), if_internalized(this),
2692 if_notinternalized(this, Label::kDeferred);
2693
2694 // Fast-case: The recorded {feedback} matches the {name}.
2695 GotoIf(WordEqual(strong_feedback, name), &if_polymorphic_name);
2696
2697 // Try to internalize the {name} if it isn't already.
2698 TryToName(name, &miss, &var_index, &if_internalized, &var_name, &miss,
2699 &if_notinternalized);
2700
2701 BIND(&if_internalized);
2702 {
2703 // The {var_name} now contains a unique name.
2704 Branch(WordEqual(strong_feedback, var_name.value()), &if_polymorphic_name,
2705 &miss);
2706 }
2707
2708 BIND(&if_notinternalized);
2709 {
2710 // Try to internalize the {name}.
2711 Node* function = ExternalConstant(
2712 ExternalReference::try_internalize_string_function());
2713 Node* const isolate_ptr =
2714 ExternalConstant(ExternalReference::isolate_address(isolate()));
2715 var_name.Bind(CallCFunction2(
2716 MachineType::AnyTagged(), MachineType::Pointer(),
2717 MachineType::AnyTagged(), function, isolate_ptr, name));
2718 Goto(&if_internalized);
2719 }
2720
2721 BIND(&if_polymorphic_name);
2722 {
2723 // If the name comparison succeeded, we know we have a weak fixed array
2724 // with at least one map/handler pair.
2725 Node* name = var_name.value();
2726 TailCallBuiltin(Builtins::kKeyedLoadIC_PolymorphicName, p->context,
2727 p->receiver, name, p->slot, p->vector);
2728 }
2729 }
2730
2731 BIND(&miss);
2732 {
2733 Comment("KeyedLoadIC_miss");
2734 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
2735 p->name, p->slot, p->vector);
2736 }
2737 }
2738
KeyedLoadICGeneric(const LoadICParameters * p)2739 void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
2740 VARIABLE(var_index, MachineType::PointerRepresentation());
2741 VARIABLE(var_unique, MachineRepresentation::kTagged);
2742 var_unique.Bind(p->name); // Dummy initialization.
2743 Label if_index(this), if_unique_name(this), if_notunique(this), slow(this);
2744
2745 Node* receiver = p->receiver;
2746 GotoIf(TaggedIsSmi(receiver), &slow);
2747 Node* receiver_map = LoadMap(receiver);
2748 Node* instance_type = LoadMapInstanceType(receiver_map);
2749
2750 TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow,
2751 &if_notunique);
2752
2753 BIND(&if_index);
2754 {
2755 GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(),
2756 &slow);
2757 }
2758
2759 BIND(&if_unique_name);
2760 {
2761 LoadICParameters pp = *p;
2762 pp.name = var_unique.value();
2763 GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow);
2764 }
2765
2766 BIND(&if_notunique);
2767 {
2768 if (FLAG_internalize_on_the_fly) {
2769 // Ideally we could return undefined directly here if the name is not
2770 // found in the string table, i.e. it was never internalized, but that
2771 // invariant doesn't hold with named property interceptors (at this
2772 // point), so we take the {slow} path instead.
2773 Label if_in_string_table(this);
2774 TryInternalizeString(p->name, &if_index, &var_index, &if_in_string_table,
2775 &var_unique, &slow, &slow);
2776
2777 BIND(&if_in_string_table);
2778 {
2779 // TODO(bmeurer): We currently use a version of GenericPropertyLoad
2780 // here, where we don't try to probe the megamorphic stub cache after
2781 // successfully internalizing the incoming string. Past experiments
2782 // with this have shown that it causes too much traffic on the stub
2783 // cache. We may want to re-evaluate that in the future.
2784 LoadICParameters pp = *p;
2785 pp.name = var_unique.value();
2786 GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow,
2787 kDontUseStubCache);
2788 }
2789 } else {
2790 Goto(&slow);
2791 }
2792 }
2793
2794 BIND(&slow);
2795 {
2796 Comment("KeyedLoadGeneric_slow");
2797 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1);
2798 // TODO(jkummerow): Should we use the GetProperty TF stub instead?
2799 TailCallRuntime(Runtime::kKeyedGetProperty, p->context, p->receiver,
2800 p->name);
2801 }
2802 }
2803
KeyedLoadICPolymorphicName(const LoadICParameters * p)2804 void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) {
2805 TVARIABLE(MaybeObject, var_handler);
2806 Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
2807
2808 Node* receiver = p->receiver;
2809 Node* receiver_map = LoadReceiverMap(receiver);
2810 Node* name = p->name;
2811 Node* vector = p->vector;
2812 Node* slot = p->slot;
2813 Node* context = p->context;
2814
2815 // When we get here, we know that the {name} matches the recorded
2816 // feedback name in the {vector} and can safely be used for the
2817 // LoadIC handler logic below.
2818 CSA_ASSERT(this, IsName(name));
2819 CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map)));
2820 CSA_ASSERT(this, WordEqual(name, CAST(LoadFeedbackVectorSlot(
2821 vector, slot, 0, SMI_PARAMETERS))));
2822
2823 // Check if we have a matching handler for the {receiver_map}.
2824 TNode<MaybeObject> feedback_element =
2825 LoadFeedbackVectorSlot(vector, slot, kPointerSize, SMI_PARAMETERS);
2826 TNode<WeakFixedArray> array = CAST(feedback_element);
2827 HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss,
2828 1);
2829
2830 BIND(&if_handler);
2831 {
2832 ExitPoint direct_exit(this);
2833 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit,
2834 ICMode::kNonGlobalIC,
2835 OnNonExistent::kReturnUndefined, kOnlyProperties);
2836 }
2837
2838 BIND(&miss);
2839 {
2840 Comment("KeyedLoadIC_miss");
2841 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, name, slot,
2842 vector);
2843 }
2844 }
2845
StoreIC(const StoreICParameters * p)2846 void AccessorAssembler::StoreIC(const StoreICParameters* p) {
2847 TVARIABLE(MaybeObject, var_handler,
2848 ReinterpretCast<MaybeObject>(SmiConstant(0)));
2849
2850 Label if_handler(this, &var_handler),
2851 if_handler_from_stub_cache(this, &var_handler, Label::kDeferred),
2852 try_polymorphic(this, Label::kDeferred),
2853 try_megamorphic(this, Label::kDeferred),
2854 try_uninitialized(this, Label::kDeferred), miss(this, Label::kDeferred);
2855
2856 Node* receiver_map = LoadReceiverMap(p->receiver);
2857 GotoIf(IsDeprecatedMap(receiver_map), &miss);
2858
2859 // Check monomorphic case.
2860 TNode<MaybeObject> feedback =
2861 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
2862 &var_handler, &try_polymorphic);
2863 BIND(&if_handler);
2864 {
2865 Comment("StoreIC_if_handler");
2866 HandleStoreICHandlerCase(p, var_handler.value(), &miss,
2867 ICMode::kNonGlobalIC);
2868 }
2869
2870 BIND(&try_polymorphic);
2871 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
2872 {
2873 // Check polymorphic case.
2874 Comment("StoreIC_try_polymorphic");
2875 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
2876 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
2877 &var_handler, &miss, 2);
2878 }
2879
2880 BIND(&try_megamorphic);
2881 {
2882 // Check megamorphic case.
2883 GotoIfNot(WordEqual(strong_feedback,
2884 LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
2885 &try_uninitialized);
2886
2887 TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name,
2888 &if_handler, &var_handler, &miss);
2889 }
2890 BIND(&try_uninitialized);
2891 {
2892 // Check uninitialized case.
2893 GotoIfNot(WordEqual(strong_feedback,
2894 LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2895 &miss);
2896 TailCallBuiltin(Builtins::kStoreIC_Uninitialized, p->context, p->receiver,
2897 p->name, p->value, p->slot, p->vector);
2898 }
2899 BIND(&miss);
2900 {
2901 TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot,
2902 p->vector, p->receiver, p->name);
2903 }
2904 }
2905
StoreGlobalIC(const StoreICParameters * pp)2906 void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) {
2907 Label if_lexical_var(this), if_property_cell(this);
2908 TNode<MaybeObject> maybe_weak_ref =
2909 LoadFeedbackVectorSlot(pp->vector, pp->slot, 0, SMI_PARAMETERS);
2910 Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell);
2911
2912 BIND(&if_property_cell);
2913 {
2914 Label try_handler(this), miss(this, Label::kDeferred);
2915 CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_weak_ref));
2916 TNode<PropertyCell> property_cell =
2917 CAST(ToWeakHeapObject(maybe_weak_ref, &try_handler));
2918
2919 ExitPoint direct_exit(this);
2920 StoreGlobalIC_PropertyCellCase(property_cell, pp->value, &direct_exit,
2921 &miss);
2922
2923 BIND(&try_handler);
2924 {
2925 Comment("StoreGlobalIC_try_handler");
2926 TNode<MaybeObject> handler = LoadFeedbackVectorSlot(
2927 pp->vector, pp->slot, kPointerSize, SMI_PARAMETERS);
2928
2929 GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
2930 &miss);
2931
2932 StoreICParameters p = *pp;
2933 DCHECK_NULL(p.receiver);
2934 Node* native_context = LoadNativeContext(p.context);
2935 p.receiver =
2936 LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX);
2937
2938 HandleStoreICHandlerCase(&p, handler, &miss, ICMode::kGlobalIC);
2939 }
2940
2941 BIND(&miss);
2942 {
2943 TailCallRuntime(Runtime::kStoreGlobalIC_Miss, pp->context, pp->value,
2944 pp->slot, pp->vector, pp->name);
2945 }
2946 }
2947
2948 BIND(&if_lexical_var);
2949 {
2950 Comment("Store lexical variable");
2951 TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref));
2952 TNode<IntPtrT> context_index =
2953 Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler));
2954 TNode<IntPtrT> slot_index =
2955 Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler));
2956 TNode<Context> script_context =
2957 LoadScriptContext(CAST(pp->context), context_index);
2958 StoreContextElement(script_context, slot_index, pp->value);
2959 Return(pp->value);
2960 }
2961 }
2962
StoreGlobalIC_PropertyCellCase(Node * property_cell,Node * value,ExitPoint * exit_point,Label * miss)2963 void AccessorAssembler::StoreGlobalIC_PropertyCellCase(Node* property_cell,
2964 Node* value,
2965 ExitPoint* exit_point,
2966 Label* miss) {
2967 Comment("StoreGlobalIC_TryPropertyCellCase");
2968 CSA_ASSERT(this, IsPropertyCell(property_cell));
2969
2970 // Load the payload of the global parameter cell. A hole indicates that
2971 // the cell has been invalidated and that the store must be handled by the
2972 // runtime.
2973 Node* cell_contents =
2974 LoadObjectField(property_cell, PropertyCell::kValueOffset);
2975 Node* details = LoadAndUntagToWord32ObjectField(property_cell,
2976 PropertyCell::kDetailsOffset);
2977 GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask), miss);
2978 CSA_ASSERT(this,
2979 Word32Equal(DecodeWord32<PropertyDetails::KindField>(details),
2980 Int32Constant(kData)));
2981
2982 Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details);
2983
2984 Label constant(this), store(this), not_smi(this);
2985
2986 GotoIf(Word32Equal(type, Int32Constant(
2987 static_cast<int>(PropertyCellType::kConstant))),
2988 &constant);
2989
2990 GotoIf(IsTheHole(cell_contents), miss);
2991
2992 GotoIf(Word32Equal(
2993 type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))),
2994 &store);
2995 CSA_ASSERT(this,
2996 Word32Or(Word32Equal(type, Int32Constant(static_cast<int>(
2997 PropertyCellType::kConstantType))),
2998 Word32Equal(type, Int32Constant(static_cast<int>(
2999 PropertyCellType::kUndefined)))));
3000
3001 GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi);
3002 GotoIfNot(TaggedIsSmi(value), miss);
3003 Goto(&store);
3004
3005 BIND(¬_smi);
3006 {
3007 GotoIf(TaggedIsSmi(value), miss);
3008 Node* expected_map = LoadMap(cell_contents);
3009 Node* map = LoadMap(value);
3010 GotoIfNot(WordEqual(expected_map, map), miss);
3011 Goto(&store);
3012 }
3013
3014 BIND(&store);
3015 {
3016 StoreObjectField(property_cell, PropertyCell::kValueOffset, value);
3017 exit_point->Return(value);
3018 }
3019
3020 BIND(&constant);
3021 {
3022 GotoIfNot(WordEqual(cell_contents, value), miss);
3023 exit_point->Return(value);
3024 }
3025 }
3026
KeyedStoreIC(const StoreICParameters * p)3027 void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) {
3028 Label miss(this, Label::kDeferred);
3029 {
3030 TVARIABLE(MaybeObject, var_handler);
3031
3032 Label if_handler(this, &var_handler),
3033 try_polymorphic(this, Label::kDeferred),
3034 try_megamorphic(this, Label::kDeferred),
3035 try_polymorphic_name(this, Label::kDeferred);
3036
3037 Node* receiver_map = LoadReceiverMap(p->receiver);
3038 GotoIf(IsDeprecatedMap(receiver_map), &miss);
3039
3040 // Check monomorphic case.
3041 TNode<MaybeObject> feedback =
3042 TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
3043 &var_handler, &try_polymorphic);
3044 BIND(&if_handler);
3045 {
3046 Comment("KeyedStoreIC_if_handler");
3047 HandleStoreICHandlerCase(p, var_handler.value(), &miss,
3048 ICMode::kNonGlobalIC, kSupportElements);
3049 }
3050
3051 BIND(&try_polymorphic);
3052 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
3053 {
3054 // CheckPolymorphic case.
3055 Comment("KeyedStoreIC_try_polymorphic");
3056 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)),
3057 &try_megamorphic);
3058 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
3059 &var_handler, &miss, 2);
3060 }
3061
3062 BIND(&try_megamorphic);
3063 {
3064 // Check megamorphic case.
3065 Comment("KeyedStoreIC_try_megamorphic");
3066 GotoIfNot(WordEqual(strong_feedback,
3067 LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
3068 &try_polymorphic_name);
3069 TailCallBuiltin(Builtins::kKeyedStoreIC_Megamorphic, p->context,
3070 p->receiver, p->name, p->value, p->slot, p->vector);
3071 }
3072
3073 BIND(&try_polymorphic_name);
3074 {
3075 // We might have a name in feedback, and a fixed array in the next slot.
3076 Comment("KeyedStoreIC_try_polymorphic_name");
3077 GotoIfNot(WordEqual(strong_feedback, p->name), &miss);
3078 // If the name comparison succeeded, we know we have a feedback vector
3079 // with at least one map/handler pair.
3080 TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(
3081 p->vector, p->slot, kPointerSize, SMI_PARAMETERS);
3082 TNode<WeakFixedArray> array = CAST(feedback_element);
3083 HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler,
3084 &miss, 1);
3085 }
3086 }
3087 BIND(&miss);
3088 {
3089 Comment("KeyedStoreIC_miss");
3090 TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
3091 p->vector, p->receiver, p->name);
3092 }
3093 }
3094
StoreInArrayLiteralIC(const StoreICParameters * p)3095 void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) {
3096 Label miss(this, Label::kDeferred);
3097 {
3098 TVARIABLE(MaybeObject, var_handler);
3099
3100 Label if_handler(this, &var_handler),
3101 try_polymorphic(this, Label::kDeferred),
3102 try_megamorphic(this, Label::kDeferred);
3103
3104 Node* array_map = LoadReceiverMap(p->receiver);
3105 GotoIf(IsDeprecatedMap(array_map), &miss);
3106 TNode<MaybeObject> feedback =
3107 TryMonomorphicCase(p->slot, p->vector, array_map, &if_handler,
3108 &var_handler, &try_polymorphic);
3109
3110 BIND(&if_handler);
3111 {
3112 Comment("StoreInArrayLiteralIC_if_handler");
3113 // This is a stripped-down version of HandleStoreICHandlerCase.
3114
3115 TNode<HeapObject> handler = CAST(var_handler.value());
3116 Label if_transitioning_element_store(this);
3117 GotoIfNot(IsCode(handler), &if_transitioning_element_store);
3118 TailCallStub(StoreWithVectorDescriptor{}, CAST(handler), CAST(p->context),
3119 p->receiver, p->name, p->value, p->slot, p->vector);
3120
3121 BIND(&if_transitioning_element_store);
3122 {
3123 TNode<MaybeObject> maybe_transition_map =
3124 LoadHandlerDataField(CAST(handler), 1);
3125 TNode<Map> transition_map =
3126 CAST(ToWeakHeapObject(maybe_transition_map, &miss));
3127 GotoIf(IsDeprecatedMap(transition_map), &miss);
3128 Node* code = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
3129 CSA_ASSERT(this, IsCode(code));
3130 TailCallStub(StoreTransitionDescriptor{}, code, p->context, p->receiver,
3131 p->name, transition_map, p->value, p->slot, p->vector);
3132 }
3133 }
3134
3135 BIND(&try_polymorphic);
3136 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
3137 {
3138 Comment("StoreInArrayLiteralIC_try_polymorphic");
3139 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)),
3140 &try_megamorphic);
3141 HandlePolymorphicCase(array_map, CAST(strong_feedback), &if_handler,
3142 &var_handler, &miss, 2);
3143 }
3144
3145 BIND(&try_megamorphic);
3146 {
3147 Comment("StoreInArrayLiteralIC_try_megamorphic");
3148 CSA_ASSERT(
3149 this,
3150 Word32Or(WordEqual(strong_feedback,
3151 LoadRoot(Heap::kuninitialized_symbolRootIndex)),
3152 WordEqual(strong_feedback,
3153 LoadRoot(Heap::kmegamorphic_symbolRootIndex))));
3154 GotoIfNot(WordEqual(strong_feedback,
3155 LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
3156 &miss);
3157 TailCallRuntime(Runtime::kStoreInArrayLiteralIC_Slow, p->context,
3158 p->value, p->receiver, p->name);
3159 }
3160 }
3161
3162 BIND(&miss);
3163 {
3164 Comment("StoreInArrayLiteralIC_miss");
3165 // TODO(neis): Introduce Runtime::kStoreInArrayLiteralIC_Miss.
3166 TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot,
3167 p->vector, p->receiver, p->name);
3168 }
3169 }
3170
3171 //////////////////// Public methods.
3172
GenerateLoadIC()3173 void AccessorAssembler::GenerateLoadIC() {
3174 typedef LoadWithVectorDescriptor Descriptor;
3175
3176 Node* receiver = Parameter(Descriptor::kReceiver);
3177 Node* name = Parameter(Descriptor::kName);
3178 Node* slot = Parameter(Descriptor::kSlot);
3179 Node* vector = Parameter(Descriptor::kVector);
3180 Node* context = Parameter(Descriptor::kContext);
3181
3182 LoadICParameters p(context, receiver, name, slot, vector);
3183 LoadIC(&p);
3184 }
3185
GenerateLoadIC_Noninlined()3186 void AccessorAssembler::GenerateLoadIC_Noninlined() {
3187 typedef LoadWithVectorDescriptor Descriptor;
3188
3189 Node* receiver = Parameter(Descriptor::kReceiver);
3190 Node* name = Parameter(Descriptor::kName);
3191 Node* slot = Parameter(Descriptor::kSlot);
3192 Node* vector = Parameter(Descriptor::kVector);
3193 Node* context = Parameter(Descriptor::kContext);
3194
3195 ExitPoint direct_exit(this);
3196 TVARIABLE(MaybeObject, var_handler);
3197 Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
3198
3199 Node* receiver_map = LoadReceiverMap(receiver);
3200 TNode<MaybeObject> feedback_element =
3201 LoadFeedbackVectorSlot(vector, slot, 0, SMI_PARAMETERS);
3202 TNode<HeapObject> feedback = CAST(feedback_element);
3203
3204 LoadICParameters p(context, receiver, name, slot, vector);
3205 LoadIC_Noninlined(&p, receiver_map, feedback, &var_handler, &if_handler,
3206 &miss, &direct_exit);
3207
3208 BIND(&if_handler);
3209 HandleLoadICHandlerCase(&p, CAST(var_handler.value()), &miss, &direct_exit);
3210
3211 BIND(&miss);
3212 direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name,
3213 slot, vector);
3214 }
3215
GenerateLoadIC_Uninitialized()3216 void AccessorAssembler::GenerateLoadIC_Uninitialized() {
3217 typedef LoadWithVectorDescriptor Descriptor;
3218
3219 Node* receiver = Parameter(Descriptor::kReceiver);
3220 Node* name = Parameter(Descriptor::kName);
3221 Node* slot = Parameter(Descriptor::kSlot);
3222 Node* vector = Parameter(Descriptor::kVector);
3223 Node* context = Parameter(Descriptor::kContext);
3224
3225 LoadICParameters p(context, receiver, name, slot, vector);
3226 LoadIC_Uninitialized(&p);
3227 }
3228
GenerateLoadICTrampoline()3229 void AccessorAssembler::GenerateLoadICTrampoline() {
3230 typedef LoadDescriptor Descriptor;
3231
3232 Node* receiver = Parameter(Descriptor::kReceiver);
3233 Node* name = Parameter(Descriptor::kName);
3234 Node* slot = Parameter(Descriptor::kSlot);
3235 Node* context = Parameter(Descriptor::kContext);
3236 Node* vector = LoadFeedbackVectorForStub();
3237
3238 TailCallBuiltin(Builtins::kLoadIC, context, receiver, name, slot, vector);
3239 }
3240
GenerateLoadGlobalIC(TypeofMode typeof_mode)3241 void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) {
3242 typedef LoadGlobalWithVectorDescriptor Descriptor;
3243
3244 Node* name = Parameter(Descriptor::kName);
3245 Node* slot = Parameter(Descriptor::kSlot);
3246 Node* vector = Parameter(Descriptor::kVector);
3247 Node* context = Parameter(Descriptor::kContext);
3248
3249 ExitPoint direct_exit(this);
3250 LoadGlobalIC(CAST(vector), slot,
3251 // lazy_context
3252 [=] { return CAST(context); },
3253 // lazy_name
3254 [=] { return CAST(name); }, typeof_mode, &direct_exit);
3255 }
3256
GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode)3257 void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) {
3258 typedef LoadGlobalDescriptor Descriptor;
3259
3260 Node* name = Parameter(Descriptor::kName);
3261 Node* slot = Parameter(Descriptor::kSlot);
3262 Node* context = Parameter(Descriptor::kContext);
3263 Node* vector = LoadFeedbackVectorForStub();
3264
3265 Callable callable =
3266 CodeFactory::LoadGlobalICInOptimizedCode(isolate(), typeof_mode);
3267 TailCallStub(callable, context, name, slot, vector);
3268 }
3269
GenerateKeyedLoadIC()3270 void AccessorAssembler::GenerateKeyedLoadIC() {
3271 typedef LoadWithVectorDescriptor Descriptor;
3272
3273 Node* receiver = Parameter(Descriptor::kReceiver);
3274 Node* name = Parameter(Descriptor::kName);
3275 Node* slot = Parameter(Descriptor::kSlot);
3276 Node* vector = Parameter(Descriptor::kVector);
3277 Node* context = Parameter(Descriptor::kContext);
3278
3279 LoadICParameters p(context, receiver, name, slot, vector);
3280 KeyedLoadIC(&p);
3281 }
3282
GenerateKeyedLoadICTrampoline()3283 void AccessorAssembler::GenerateKeyedLoadICTrampoline() {
3284 typedef LoadDescriptor Descriptor;
3285
3286 Node* receiver = Parameter(Descriptor::kReceiver);
3287 Node* name = Parameter(Descriptor::kName);
3288 Node* slot = Parameter(Descriptor::kSlot);
3289 Node* context = Parameter(Descriptor::kContext);
3290 Node* vector = LoadFeedbackVectorForStub();
3291
3292 TailCallBuiltin(Builtins::kKeyedLoadIC, context, receiver, name, slot,
3293 vector);
3294 }
3295
GenerateKeyedLoadIC_Megamorphic()3296 void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() {
3297 typedef LoadWithVectorDescriptor Descriptor;
3298
3299 Node* receiver = Parameter(Descriptor::kReceiver);
3300 Node* name = Parameter(Descriptor::kName);
3301 Node* slot = Parameter(Descriptor::kSlot);
3302 Node* vector = Parameter(Descriptor::kVector);
3303 Node* context = Parameter(Descriptor::kContext);
3304
3305 LoadICParameters p(context, receiver, name, slot, vector);
3306 KeyedLoadICGeneric(&p);
3307 }
3308
GenerateKeyedLoadIC_PolymorphicName()3309 void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() {
3310 typedef LoadWithVectorDescriptor Descriptor;
3311
3312 Node* receiver = Parameter(Descriptor::kReceiver);
3313 Node* name = Parameter(Descriptor::kName);
3314 Node* slot = Parameter(Descriptor::kSlot);
3315 Node* vector = Parameter(Descriptor::kVector);
3316 Node* context = Parameter(Descriptor::kContext);
3317
3318 LoadICParameters p(context, receiver, name, slot, vector);
3319 KeyedLoadICPolymorphicName(&p);
3320 }
3321
GenerateStoreGlobalIC()3322 void AccessorAssembler::GenerateStoreGlobalIC() {
3323 typedef StoreGlobalWithVectorDescriptor Descriptor;
3324
3325 Node* name = Parameter(Descriptor::kName);
3326 Node* value = Parameter(Descriptor::kValue);
3327 Node* slot = Parameter(Descriptor::kSlot);
3328 Node* vector = Parameter(Descriptor::kVector);
3329 Node* context = Parameter(Descriptor::kContext);
3330
3331 StoreICParameters p(context, nullptr, name, value, slot, vector);
3332 StoreGlobalIC(&p);
3333 }
3334
GenerateStoreGlobalICTrampoline()3335 void AccessorAssembler::GenerateStoreGlobalICTrampoline() {
3336 typedef StoreGlobalDescriptor Descriptor;
3337
3338 Node* name = Parameter(Descriptor::kName);
3339 Node* value = Parameter(Descriptor::kValue);
3340 Node* slot = Parameter(Descriptor::kSlot);
3341 Node* context = Parameter(Descriptor::kContext);
3342 Node* vector = LoadFeedbackVectorForStub();
3343
3344 TailCallBuiltin(Builtins::kStoreGlobalIC, context, name, value, slot, vector);
3345 }
3346
GenerateStoreIC()3347 void AccessorAssembler::GenerateStoreIC() {
3348 typedef StoreWithVectorDescriptor Descriptor;
3349
3350 Node* receiver = Parameter(Descriptor::kReceiver);
3351 Node* name = Parameter(Descriptor::kName);
3352 Node* value = Parameter(Descriptor::kValue);
3353 Node* slot = Parameter(Descriptor::kSlot);
3354 Node* vector = Parameter(Descriptor::kVector);
3355 Node* context = Parameter(Descriptor::kContext);
3356
3357 StoreICParameters p(context, receiver, name, value, slot, vector);
3358 StoreIC(&p);
3359 }
3360
GenerateStoreICTrampoline()3361 void AccessorAssembler::GenerateStoreICTrampoline() {
3362 typedef StoreDescriptor Descriptor;
3363
3364 Node* receiver = Parameter(Descriptor::kReceiver);
3365 Node* name = Parameter(Descriptor::kName);
3366 Node* value = Parameter(Descriptor::kValue);
3367 Node* slot = Parameter(Descriptor::kSlot);
3368 Node* context = Parameter(Descriptor::kContext);
3369 Node* vector = LoadFeedbackVectorForStub();
3370
3371 TailCallBuiltin(Builtins::kStoreIC, context, receiver, name, value, slot,
3372 vector);
3373 }
3374
GenerateKeyedStoreIC()3375 void AccessorAssembler::GenerateKeyedStoreIC() {
3376 typedef StoreWithVectorDescriptor Descriptor;
3377
3378 Node* receiver = Parameter(Descriptor::kReceiver);
3379 Node* name = Parameter(Descriptor::kName);
3380 Node* value = Parameter(Descriptor::kValue);
3381 Node* slot = Parameter(Descriptor::kSlot);
3382 Node* vector = Parameter(Descriptor::kVector);
3383 Node* context = Parameter(Descriptor::kContext);
3384
3385 StoreICParameters p(context, receiver, name, value, slot, vector);
3386 KeyedStoreIC(&p);
3387 }
3388
GenerateKeyedStoreICTrampoline()3389 void AccessorAssembler::GenerateKeyedStoreICTrampoline() {
3390 typedef StoreDescriptor Descriptor;
3391
3392 Node* receiver = Parameter(Descriptor::kReceiver);
3393 Node* name = Parameter(Descriptor::kName);
3394 Node* value = Parameter(Descriptor::kValue);
3395 Node* slot = Parameter(Descriptor::kSlot);
3396 Node* context = Parameter(Descriptor::kContext);
3397 Node* vector = LoadFeedbackVectorForStub();
3398
3399 TailCallBuiltin(Builtins::kKeyedStoreIC, context, receiver, name, value, slot,
3400 vector);
3401 }
3402
GenerateStoreInArrayLiteralIC()3403 void AccessorAssembler::GenerateStoreInArrayLiteralIC() {
3404 typedef StoreWithVectorDescriptor Descriptor;
3405
3406 Node* array = Parameter(Descriptor::kReceiver);
3407 Node* index = Parameter(Descriptor::kName);
3408 Node* value = Parameter(Descriptor::kValue);
3409 Node* slot = Parameter(Descriptor::kSlot);
3410 Node* vector = Parameter(Descriptor::kVector);
3411 Node* context = Parameter(Descriptor::kContext);
3412
3413 StoreICParameters p(context, array, index, value, slot, vector);
3414 StoreInArrayLiteralIC(&p);
3415 }
3416
GenerateCloneObjectIC()3417 void AccessorAssembler::GenerateCloneObjectIC() {
3418 typedef CloneObjectWithVectorDescriptor Descriptor;
3419 Node* source = Parameter(Descriptor::kSource);
3420 Node* flags = Parameter(Descriptor::kFlags);
3421 Node* slot = Parameter(Descriptor::kSlot);
3422 Node* vector = Parameter(Descriptor::kVector);
3423 Node* context = Parameter(Descriptor::kContext);
3424 TVARIABLE(MaybeObject, var_handler);
3425 Label if_handler(this, &var_handler);
3426 Label miss(this, Label::kDeferred), try_polymorphic(this, Label::kDeferred),
3427 try_megamorphic(this, Label::kDeferred);
3428
3429 CSA_SLOW_ASSERT(this, TaggedIsNotSmi(source));
3430 Node* source_map = LoadMap(UncheckedCast<HeapObject>(source));
3431 GotoIf(IsDeprecatedMap(source_map), &miss);
3432 TNode<MaybeObject> feedback = TryMonomorphicCase(
3433 slot, vector, source_map, &if_handler, &var_handler, &try_polymorphic);
3434
3435 BIND(&if_handler);
3436 {
3437 Comment("CloneObjectIC_if_handler");
3438
3439 // Handlers for the CloneObjectIC stub are weak references to the Map of
3440 // a result object.
3441 TNode<Map> result_map = CAST(var_handler.value());
3442 TVARIABLE(Object, var_properties, EmptyFixedArrayConstant());
3443 TVARIABLE(FixedArrayBase, var_elements, EmptyFixedArrayConstant());
3444
3445 Label allocate_object(this);
3446 GotoIf(IsNullOrUndefined(source), &allocate_object);
3447 CSA_SLOW_ASSERT(this, IsJSObjectMap(result_map));
3448
3449 // The IC fast case should only be taken if the result map a compatible
3450 // elements kind with the source object.
3451 TNode<FixedArrayBase> source_elements = LoadElements(source);
3452
3453 auto flags = ExtractFixedArrayFlag::kAllFixedArraysDontCopyCOW;
3454 var_elements = CAST(CloneFixedArray(source_elements, flags));
3455
3456 // Copy the PropertyArray backing store. The source PropertyArray must be
3457 // either an Smi, or a PropertyArray.
3458 // FIXME: Make a CSA macro for this
3459 TNode<Object> source_properties =
3460 LoadObjectField(source, JSObject::kPropertiesOrHashOffset);
3461 {
3462 GotoIf(TaggedIsSmi(source_properties), &allocate_object);
3463 GotoIf(IsEmptyFixedArray(source_properties), &allocate_object);
3464
3465 // This IC requires that the source object has fast properties
3466 CSA_SLOW_ASSERT(this, IsPropertyArray(CAST(source_properties)));
3467 TNode<IntPtrT> length = LoadPropertyArrayLength(
3468 UncheckedCast<PropertyArray>(source_properties));
3469 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &allocate_object);
3470
3471 auto mode = INTPTR_PARAMETERS;
3472 var_properties = CAST(AllocatePropertyArray(length, mode));
3473 CopyPropertyArrayValues(source_properties, var_properties.value(), length,
3474 SKIP_WRITE_BARRIER, mode);
3475 }
3476
3477 Goto(&allocate_object);
3478 BIND(&allocate_object);
3479 TNode<JSObject> object = UncheckedCast<JSObject>(AllocateJSObjectFromMap(
3480 result_map, var_properties.value(), var_elements.value()));
3481 ReturnIf(IsNullOrUndefined(source), object);
3482
3483 // Lastly, clone any in-object properties.
3484 // Determine the inobject property capacity of both objects, and copy the
3485 // smaller number into the resulting object.
3486 Node* source_start = LoadMapInobjectPropertiesStartInWords(source_map);
3487 Node* source_size = LoadMapInstanceSizeInWords(source_map);
3488 Node* result_start = LoadMapInobjectPropertiesStartInWords(result_map);
3489 Node* field_offset_difference =
3490 TimesPointerSize(IntPtrSub(result_start, source_start));
3491 BuildFastLoop(source_start, source_size,
3492 [=](Node* field_index) {
3493 Node* field_offset = TimesPointerSize(field_index);
3494 Node* field = LoadObjectField(source, field_offset);
3495 Node* result_offset =
3496 IntPtrAdd(field_offset, field_offset_difference);
3497 StoreObjectFieldNoWriteBarrier(object, result_offset,
3498 field);
3499 },
3500 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
3501 Return(object);
3502 }
3503
3504 BIND(&try_polymorphic);
3505 TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss);
3506 {
3507 Comment("CloneObjectIC_try_polymorphic");
3508 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
3509 HandlePolymorphicCase(source_map, CAST(strong_feedback), &if_handler,
3510 &var_handler, &miss, 2);
3511 }
3512
3513 BIND(&try_megamorphic);
3514 {
3515 Comment("CloneObjectIC_try_megamorphic");
3516 CSA_ASSERT(
3517 this,
3518 Word32Or(WordEqual(strong_feedback,
3519 LoadRoot(Heap::kuninitialized_symbolRootIndex)),
3520 WordEqual(strong_feedback,
3521 LoadRoot(Heap::kmegamorphic_symbolRootIndex))));
3522 GotoIfNot(WordEqual(strong_feedback,
3523 LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
3524 &miss);
3525 TailCallRuntime(Runtime::kCloneObjectIC_Slow, context, source, flags);
3526 }
3527
3528 BIND(&miss);
3529 {
3530 Comment("CloneObjectIC_miss");
3531 Node* map_or_result = CallRuntime(Runtime::kCloneObjectIC_Miss, context,
3532 source, flags, slot, vector);
3533 var_handler = UncheckedCast<MaybeObject>(map_or_result);
3534 GotoIf(IsMap(map_or_result), &if_handler);
3535 CSA_ASSERT(this, IsJSObject(map_or_result));
3536 Return(map_or_result);
3537 }
3538 }
3539
3540 } // namespace internal
3541 } // namespace v8
3542