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/ast/ast.h"
8 #include "src/base/optional.h"
9 #include "src/builtins/builtins-constructor-gen.h"
10 #include "src/codegen/code-factory.h"
11 #include "src/ic/handler-configuration.h"
12 #include "src/ic/ic.h"
13 #include "src/ic/keyed-store-generic.h"
14 #include "src/ic/stub-cache.h"
15 #include "src/logging/counters.h"
16 #include "src/objects/cell.h"
17 #include "src/objects/foreign.h"
18 #include "src/objects/heap-number.h"
19 #include "src/objects/module.h"
20 #include "src/objects/objects-inl.h"
21 #include "src/objects/property-details.h"
22 #include "src/objects/smi.h"
23
24 namespace v8 {
25 namespace internal {
26
27 using compiler::CodeAssemblerState;
28 using compiler::Node;
29
30 //////////////////// Private helpers.
31
32 // Loads dataX field from the DataHandler object.
LoadHandlerDataField(TNode<DataHandler> handler,int data_index)33 TNode<MaybeObject> AccessorAssembler::LoadHandlerDataField(
34 TNode<DataHandler> handler, int data_index) {
35 #ifdef DEBUG
36 TNode<Map> handler_map = LoadMap(handler);
37 TNode<Uint16T> instance_type = LoadMapInstanceType(handler_map);
38 #endif
39 CSA_ASSERT(this,
40 Word32Or(InstanceTypeEqual(instance_type, LOAD_HANDLER_TYPE),
41 InstanceTypeEqual(instance_type, STORE_HANDLER_TYPE)));
42 int offset = 0;
43 int minimum_size = 0;
44 switch (data_index) {
45 case 1:
46 offset = DataHandler::kData1Offset;
47 minimum_size = DataHandler::kSizeWithData1;
48 break;
49 case 2:
50 offset = DataHandler::kData2Offset;
51 minimum_size = DataHandler::kSizeWithData2;
52 break;
53 case 3:
54 offset = DataHandler::kData3Offset;
55 minimum_size = DataHandler::kSizeWithData3;
56 break;
57 default:
58 UNREACHABLE();
59 }
60 USE(minimum_size);
61 CSA_ASSERT(this, UintPtrGreaterThanOrEqual(
62 LoadMapInstanceSizeInWords(handler_map),
63 IntPtrConstant(minimum_size / kTaggedSize)));
64 return LoadMaybeWeakObjectField(handler, offset);
65 }
66
TryMonomorphicCase(TNode<TaggedIndex> slot,TNode<FeedbackVector> vector,TNode<Map> lookup_start_object_map,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)67 TNode<MaybeObject> AccessorAssembler::TryMonomorphicCase(
68 TNode<TaggedIndex> slot, TNode<FeedbackVector> vector,
69 TNode<Map> lookup_start_object_map, Label* if_handler,
70 TVariable<MaybeObject>* var_handler, Label* if_miss) {
71 Comment("TryMonomorphicCase");
72 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
73
74 // TODO(ishell): add helper class that hides offset computations for a series
75 // of loads.
76 int32_t header_size =
77 FeedbackVector::kRawFeedbackSlotsOffset - kHeapObjectTag;
78 // Adding |header_size| with a separate IntPtrAdd rather than passing it
79 // into ElementOffsetFromIndex() allows it to be folded into a single
80 // [base, index, offset] indirect memory access on x64.
81 TNode<IntPtrT> offset = ElementOffsetFromIndex(slot, HOLEY_ELEMENTS);
82 TNode<MaybeObject> feedback = ReinterpretCast<MaybeObject>(
83 Load(MachineType::AnyTagged(), vector,
84 IntPtrAdd(offset, IntPtrConstant(header_size))));
85
86 // Try to quickly handle the monomorphic case without knowing for sure
87 // if we have a weak reference in feedback.
88 GotoIfNot(IsWeakReferenceTo(feedback, lookup_start_object_map), if_miss);
89
90 TNode<MaybeObject> handler = UncheckedCast<MaybeObject>(
91 Load(MachineType::AnyTagged(), vector,
92 IntPtrAdd(offset, IntPtrConstant(header_size + kTaggedSize))));
93
94 *var_handler = handler;
95 Goto(if_handler);
96 return feedback;
97 }
98
HandlePolymorphicCase(TNode<Map> lookup_start_object_map,TNode<WeakFixedArray> feedback,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)99 void AccessorAssembler::HandlePolymorphicCase(
100 TNode<Map> lookup_start_object_map, TNode<WeakFixedArray> feedback,
101 Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss) {
102 Comment("HandlePolymorphicCase");
103 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
104
105 // Iterate {feedback} array.
106 const int kEntrySize = 2;
107
108 // Load the {feedback} array length.
109 TNode<IntPtrT> length = LoadAndUntagWeakFixedArrayLength(feedback);
110 CSA_ASSERT(this, IntPtrLessThanOrEqual(IntPtrConstant(kEntrySize), length));
111
112 // This is a hand-crafted loop that iterates backwards and only compares
113 // against zero at the end, since we already know that we will have at least a
114 // single entry in the {feedback} array anyways.
115 TVARIABLE(IntPtrT, var_index, IntPtrSub(length, IntPtrConstant(kEntrySize)));
116 Label loop(this, &var_index), loop_next(this);
117 Goto(&loop);
118 BIND(&loop);
119 {
120 TNode<MaybeObject> maybe_cached_map =
121 LoadWeakFixedArrayElement(feedback, var_index.value());
122 CSA_ASSERT(this, IsWeakOrCleared(maybe_cached_map));
123 GotoIfNot(IsWeakReferenceTo(maybe_cached_map, lookup_start_object_map),
124 &loop_next);
125
126 // Found, now call handler.
127 TNode<MaybeObject> handler =
128 LoadWeakFixedArrayElement(feedback, var_index.value(), kTaggedSize);
129 *var_handler = handler;
130 Goto(if_handler);
131
132 BIND(&loop_next);
133 var_index =
134 Signed(IntPtrSub(var_index.value(), IntPtrConstant(kEntrySize)));
135 Branch(IntPtrGreaterThanOrEqual(var_index.value(), IntPtrConstant(0)),
136 &loop, if_miss);
137 }
138 }
139
HandleLoadICHandlerCase(const LazyLoadICParameters * p,TNode<Object> handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode,OnNonExistent on_nonexistent,ElementSupport support_elements,LoadAccessMode access_mode)140 void AccessorAssembler::HandleLoadICHandlerCase(
141 const LazyLoadICParameters* p, TNode<Object> handler, Label* miss,
142 ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent,
143 ElementSupport support_elements, LoadAccessMode access_mode) {
144 Comment("have_handler");
145
146 TVARIABLE(Object, var_holder, p->lookup_start_object());
147 TVARIABLE(Object, var_smi_handler, handler);
148
149 Label if_smi_handler(this, {&var_holder, &var_smi_handler});
150 Label try_proto_handler(this, Label::kDeferred),
151 call_handler(this, Label::kDeferred);
152
153 Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler);
154
155 BIND(&try_proto_handler);
156 {
157 GotoIf(IsCodeMap(LoadMap(CAST(handler))), &call_handler);
158 HandleLoadICProtoHandler(p, CAST(handler), &var_holder, &var_smi_handler,
159 &if_smi_handler, miss, exit_point, ic_mode,
160 access_mode);
161 }
162
163 // |handler| is a Smi, encoding what to do. See SmiHandler methods
164 // for the encoding format.
165 BIND(&if_smi_handler);
166 {
167 HandleLoadICSmiHandlerCase(
168 p, var_holder.value(), CAST(var_smi_handler.value()), handler, miss,
169 exit_point, ic_mode, on_nonexistent, support_elements, access_mode);
170 }
171
172 BIND(&call_handler);
173 {
174 exit_point->ReturnCallStub(LoadWithVectorDescriptor{}, CAST(handler),
175 p->context(), p->receiver(), p->name(),
176 p->slot(), p->vector());
177 }
178 }
179
HandleLoadCallbackProperty(const LazyLoadICParameters * p,TNode<JSObject> holder,TNode<WordT> handler_word,ExitPoint * exit_point)180 void AccessorAssembler::HandleLoadCallbackProperty(
181 const LazyLoadICParameters* p, TNode<JSObject> holder,
182 TNode<WordT> handler_word, ExitPoint* exit_point) {
183 Comment("native_data_property_load");
184 TNode<IntPtrT> descriptor =
185 Signed(DecodeWord<LoadHandler::DescriptorBits>(handler_word));
186
187 Callable callable = CodeFactory::ApiGetter(isolate());
188 TNode<AccessorInfo> accessor_info =
189 CAST(LoadDescriptorValue(LoadMap(holder), descriptor));
190
191 exit_point->ReturnCallStub(callable, p->context(), p->receiver(), holder,
192 accessor_info);
193 }
194
HandleLoadAccessor(const LazyLoadICParameters * p,TNode<CallHandlerInfo> call_handler_info,TNode<WordT> handler_word,TNode<DataHandler> handler,TNode<IntPtrT> handler_kind,ExitPoint * exit_point)195 void AccessorAssembler::HandleLoadAccessor(
196 const LazyLoadICParameters* p, TNode<CallHandlerInfo> call_handler_info,
197 TNode<WordT> handler_word, TNode<DataHandler> handler,
198 TNode<IntPtrT> handler_kind, ExitPoint* exit_point) {
199 Comment("api_getter");
200 // Context is stored either in data2 or data3 field depending on whether
201 // the access check is enabled for this handler or not.
202 TNode<MaybeObject> maybe_context = Select<MaybeObject>(
203 IsSetWord<LoadHandler::DoAccessCheckOnLookupStartObjectBits>(
204 handler_word),
205 [=] { return LoadHandlerDataField(handler, 3); },
206 [=] { return LoadHandlerDataField(handler, 2); });
207
208 CSA_ASSERT(this, IsWeakOrCleared(maybe_context));
209 CSA_CHECK(this, IsNotCleared(maybe_context));
210 TNode<HeapObject> context = GetHeapObjectAssumeWeak(maybe_context);
211
212 TNode<Foreign> foreign = LoadObjectField<Foreign>(
213 call_handler_info, CallHandlerInfo::kJsCallbackOffset);
214 TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
215 TNode<Object> data =
216 LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
217
218 TVARIABLE(HeapObject, api_holder, CAST(p->lookup_start_object()));
219 Label load(this);
220 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
221 &load);
222
223 CSA_ASSERT(
224 this,
225 WordEqual(handler_kind,
226 IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)));
227
228 api_holder = LoadMapPrototype(LoadMap(CAST(p->lookup_start_object())));
229 Goto(&load);
230
231 BIND(&load);
232 TNode<IntPtrT> argc = IntPtrConstant(0);
233 exit_point->Return(CallApiCallback(context, callback, argc, data,
234 api_holder.value(), p->receiver()));
235 }
236
HandleLoadField(TNode<JSObject> holder,TNode<WordT> handler_word,TVariable<Float64T> * var_double_value,Label * rebox_double,Label * miss,ExitPoint * exit_point)237 void AccessorAssembler::HandleLoadField(TNode<JSObject> holder,
238 TNode<WordT> handler_word,
239 TVariable<Float64T>* var_double_value,
240 Label* rebox_double, Label* miss,
241 ExitPoint* exit_point) {
242 Comment("field_load");
243 TNode<IntPtrT> index =
244 Signed(DecodeWord<LoadHandler::FieldIndexBits>(handler_word));
245 TNode<IntPtrT> offset = IntPtrMul(index, IntPtrConstant(kTaggedSize));
246
247 Label inobject(this), out_of_object(this);
248 Branch(IsSetWord<LoadHandler::IsInobjectBits>(handler_word), &inobject,
249 &out_of_object);
250
251 BIND(&inobject);
252 {
253 Label is_double(this);
254 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
255 exit_point->Return(LoadObjectField(holder, offset));
256
257 BIND(&is_double);
258 if (FLAG_unbox_double_fields) {
259 *var_double_value = LoadObjectField<Float64T>(holder, offset);
260 } else {
261 TNode<Object> heap_number = LoadObjectField(holder, offset);
262 // This is not an "old" Smi value from before a Smi->Double transition.
263 // Rather, it's possible that since the last update of this IC, the Double
264 // field transitioned to a Tagged field, and was then assigned a Smi.
265 GotoIf(TaggedIsSmi(heap_number), miss);
266 GotoIfNot(IsHeapNumber(CAST(heap_number)), miss);
267 *var_double_value = LoadHeapNumberValue(CAST(heap_number));
268 }
269 Goto(rebox_double);
270 }
271
272 BIND(&out_of_object);
273 {
274 Label is_double(this);
275 TNode<HeapObject> properties = LoadFastProperties(holder);
276 TNode<Object> value = LoadObjectField(properties, offset);
277 GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
278 exit_point->Return(value);
279
280 BIND(&is_double);
281 if (!FLAG_unbox_double_fields) {
282 // This is not an "old" Smi value from before a Smi->Double transition.
283 // Rather, it's possible that since the last update of this IC, the Double
284 // field transitioned to a Tagged field, and was then assigned a Smi.
285 GotoIf(TaggedIsSmi(value), miss);
286 GotoIfNot(IsHeapNumber(CAST(value)), miss);
287 }
288 *var_double_value = LoadHeapNumberValue(CAST(value));
289 Goto(rebox_double);
290 }
291 }
292
LoadDescriptorValue(TNode<Map> map,TNode<IntPtrT> descriptor_entry)293 TNode<Object> AccessorAssembler::LoadDescriptorValue(
294 TNode<Map> map, TNode<IntPtrT> descriptor_entry) {
295 return CAST(LoadDescriptorValueOrFieldType(map, descriptor_entry));
296 }
297
LoadDescriptorValueOrFieldType(TNode<Map> map,TNode<IntPtrT> descriptor_entry)298 TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType(
299 TNode<Map> map, TNode<IntPtrT> descriptor_entry) {
300 TNode<DescriptorArray> descriptors = LoadMapDescriptors(map);
301 return LoadFieldTypeByDescriptorEntry(descriptors, descriptor_entry);
302 }
303
HandleLoadICSmiHandlerCase(const LazyLoadICParameters * p,TNode<Object> holder,TNode<Smi> smi_handler,TNode<Object> handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode,OnNonExistent on_nonexistent,ElementSupport support_elements,LoadAccessMode access_mode)304 void AccessorAssembler::HandleLoadICSmiHandlerCase(
305 const LazyLoadICParameters* p, TNode<Object> holder, TNode<Smi> smi_handler,
306 TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode,
307 OnNonExistent on_nonexistent, ElementSupport support_elements,
308 LoadAccessMode access_mode) {
309 TVARIABLE(Float64T, var_double_value);
310 Label rebox_double(this, &var_double_value);
311
312 TNode<IntPtrT> handler_word = SmiUntag(smi_handler);
313 TNode<IntPtrT> handler_kind =
314 Signed(DecodeWord<LoadHandler::KindBits>(handler_word));
315
316 if (support_elements == kSupportElements) {
317 Label if_element(this), if_indexed_string(this), if_property(this),
318 if_hole(this), unimplemented_elements_kind(this),
319 if_oob(this, Label::kDeferred), try_string_to_array_index(this),
320 emit_element_load(this);
321 TVARIABLE(IntPtrT, var_intptr_index);
322 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
323 &if_element);
324
325 if (access_mode == LoadAccessMode::kHas) {
326 CSA_ASSERT(this,
327 WordNotEqual(handler_kind,
328 IntPtrConstant(LoadHandler::kIndexedString)));
329 Goto(&if_property);
330 } else {
331 Branch(
332 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)),
333 &if_indexed_string, &if_property);
334 }
335
336 BIND(&if_element);
337 {
338 Comment("element_load");
339 TVARIABLE(Int32T, var_instance_type);
340 TNode<IntPtrT> intptr_index = TryToIntptr(
341 p->name(), &try_string_to_array_index, &var_instance_type);
342 var_intptr_index = intptr_index;
343 Goto(&emit_element_load);
344
345 BIND(&try_string_to_array_index);
346 {
347 GotoIfNot(IsStringInstanceType(var_instance_type.value()), miss);
348
349 TNode<ExternalReference> function = ExternalConstant(
350 ExternalReference::string_to_array_index_function());
351 TNode<Int32T> result = UncheckedCast<Int32T>(
352 CallCFunction(function, MachineType::Int32(),
353 std::make_pair(MachineType::AnyTagged(), p->name())));
354 GotoIf(Word32Equal(Int32Constant(-1), result), miss);
355 CSA_ASSERT(this, Int32GreaterThanOrEqual(result, Int32Constant(0)));
356 var_intptr_index = ChangeInt32ToIntPtr(result);
357
358 Goto(&emit_element_load);
359 }
360
361 BIND(&emit_element_load);
362 {
363 TNode<BoolT> is_jsarray_condition =
364 IsSetWord<LoadHandler::IsJsArrayBits>(handler_word);
365 TNode<Uint32T> elements_kind =
366 DecodeWord32FromWord<LoadHandler::ElementsKindBits>(handler_word);
367 EmitElementLoad(CAST(holder), elements_kind, var_intptr_index.value(),
368 is_jsarray_condition, &if_hole, &rebox_double,
369 &var_double_value, &unimplemented_elements_kind,
370 &if_oob, miss, exit_point, access_mode);
371 }
372 }
373
374 BIND(&unimplemented_elements_kind);
375 {
376 // Smi handlers should only be installed for supported elements kinds.
377 // Crash if we get here.
378 DebugBreak();
379 Goto(miss);
380 }
381
382 BIND(&if_oob);
383 {
384 Comment("out of bounds elements access");
385 Label return_undefined(this);
386
387 // Check if we're allowed to handle OOB accesses.
388 TNode<BoolT> allow_out_of_bounds =
389 IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
390 GotoIfNot(allow_out_of_bounds, miss);
391
392 // Negative indices aren't valid array indices (according to
393 // the ECMAScript specification), and are stored as properties
394 // in V8, not elements. So we cannot handle them here, except
395 // in case of typed arrays, where integer indexed properties
396 // aren't looked up in the prototype chain.
397 GotoIf(IsJSTypedArray(CAST(holder)), &return_undefined);
398 if (Is64()) {
399 GotoIfNot(
400 UintPtrLessThanOrEqual(var_intptr_index.value(),
401 IntPtrConstant(JSArray::kMaxArrayIndex)),
402 miss);
403 } else {
404 GotoIf(IntPtrLessThan(var_intptr_index.value(), IntPtrConstant(0)),
405 miss);
406 }
407
408 // For all other receivers we need to check that the prototype chain
409 // doesn't contain any elements.
410 BranchIfPrototypesHaveNoElements(LoadMap(CAST(holder)), &return_undefined,
411 miss);
412
413 BIND(&return_undefined);
414 exit_point->Return(access_mode == LoadAccessMode::kHas
415 ? FalseConstant()
416 : UndefinedConstant());
417 }
418
419 BIND(&if_hole);
420 {
421 Comment("convert hole");
422
423 GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
424 GotoIf(IsNoElementsProtectorCellInvalid(), miss);
425 exit_point->Return(access_mode == LoadAccessMode::kHas
426 ? FalseConstant()
427 : UndefinedConstant());
428 }
429
430 if (access_mode != LoadAccessMode::kHas) {
431 BIND(&if_indexed_string);
432 {
433 Label if_oob(this, Label::kDeferred);
434
435 Comment("indexed string");
436 TNode<String> string_holder = CAST(holder);
437 TNode<UintPtrT> index = Unsigned(TryToIntptr(p->name(), miss));
438 TNode<UintPtrT> length =
439 Unsigned(LoadStringLengthAsWord(string_holder));
440 GotoIf(UintPtrGreaterThanOrEqual(index, length), &if_oob);
441 TNode<Int32T> code = StringCharCodeAt(string_holder, index);
442 TNode<String> result = StringFromSingleCharCode(code);
443 Return(result);
444
445 BIND(&if_oob);
446 TNode<BoolT> allow_out_of_bounds =
447 IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word);
448 GotoIfNot(allow_out_of_bounds, miss);
449 GotoIf(IsNoElementsProtectorCellInvalid(), miss);
450 Return(UndefinedConstant());
451 }
452 }
453
454 BIND(&if_property);
455 Comment("property_load");
456 }
457
458 if (access_mode == LoadAccessMode::kHas) {
459 HandleLoadICSmiHandlerHasNamedCase(p, holder, handler_kind, miss,
460 exit_point, ic_mode);
461 } else {
462 HandleLoadICSmiHandlerLoadNamedCase(
463 p, holder, handler_kind, handler_word, &rebox_double, &var_double_value,
464 handler, miss, exit_point, ic_mode, on_nonexistent, support_elements);
465 }
466 }
467
HandleLoadICSmiHandlerLoadNamedCase(const LazyLoadICParameters * p,TNode<Object> holder,TNode<IntPtrT> handler_kind,TNode<WordT> handler_word,Label * rebox_double,TVariable<Float64T> * var_double_value,TNode<Object> handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode,OnNonExistent on_nonexistent,ElementSupport support_elements)468 void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase(
469 const LazyLoadICParameters* p, TNode<Object> holder,
470 TNode<IntPtrT> handler_kind, TNode<WordT> handler_word, Label* rebox_double,
471 TVariable<Float64T>* var_double_value, TNode<Object> handler, Label* miss,
472 ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent,
473 ElementSupport support_elements) {
474 Label constant(this), field(this), normal(this, Label::kDeferred),
475 slow(this, Label::kDeferred), interceptor(this, Label::kDeferred),
476 nonexistent(this), accessor(this, Label::kDeferred),
477 global(this, Label::kDeferred), module_export(this, Label::kDeferred),
478 proxy(this, Label::kDeferred),
479 native_data_property(this, Label::kDeferred),
480 api_getter(this, Label::kDeferred);
481
482 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
483
484 GotoIf(WordEqual(handler_kind,
485 IntPtrConstant(LoadHandler::kConstantFromPrototype)),
486 &constant);
487
488 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
489 &nonexistent);
490
491 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)),
492 &normal);
493
494 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
495 &accessor);
496
497 GotoIf(
498 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)),
499 &native_data_property);
500
501 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
502 &api_getter);
503
504 GotoIf(WordEqual(handler_kind,
505 IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)),
506 &api_getter);
507
508 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)),
509 &global);
510
511 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kSlow)), &slow);
512
513 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy);
514
515 Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kModuleExport)),
516 &module_export, &interceptor);
517
518 BIND(&field);
519 HandleLoadField(CAST(holder), handler_word, var_double_value, rebox_double,
520 miss, exit_point);
521
522 BIND(&nonexistent);
523 // This is a handler for a load of a non-existent value.
524 if (on_nonexistent == OnNonExistent::kThrowReferenceError) {
525 exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context(),
526 p->name());
527 } else {
528 DCHECK_EQ(OnNonExistent::kReturnUndefined, on_nonexistent);
529 exit_point->Return(UndefinedConstant());
530 }
531
532 BIND(&constant);
533 {
534 Comment("constant_load");
535 exit_point->Return(holder);
536 }
537
538 BIND(&normal);
539 {
540 Comment("load_normal");
541 TNode<NameDictionary> properties = CAST(LoadSlowProperties(CAST(holder)));
542 TVARIABLE(IntPtrT, var_name_index);
543 Label found(this, &var_name_index);
544 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()), &found,
545 &var_name_index, miss);
546 BIND(&found);
547 {
548 TVARIABLE(Uint32T, var_details);
549 TVARIABLE(Object, var_value);
550 LoadPropertyFromNameDictionary(properties, var_name_index.value(),
551 &var_details, &var_value);
552 TNode<Object> value = CallGetterIfAccessor(
553 var_value.value(), CAST(holder), var_details.value(), p->context(),
554 p->receiver(), miss);
555 exit_point->Return(value);
556 }
557 }
558
559 BIND(&accessor);
560 {
561 Comment("accessor_load");
562 TNode<IntPtrT> descriptor =
563 Signed(DecodeWord<LoadHandler::DescriptorBits>(handler_word));
564 TNode<AccessorPair> accessor_pair =
565 CAST(LoadDescriptorValue(LoadMap(CAST(holder)), descriptor));
566 TNode<Object> getter =
567 LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
568 CSA_ASSERT(this, Word32BinaryNot(IsTheHole(getter)));
569
570 exit_point->Return(Call(p->context(), getter, p->receiver()));
571 }
572
573 BIND(&native_data_property);
574 HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point);
575
576 BIND(&api_getter);
577 HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler), handler_kind,
578 exit_point);
579
580 BIND(&proxy);
581 {
582 // TODO(mythria): LoadGlobals don't use this path. LoadGlobals need special
583 // handling with proxies which is currently not supported by builtins. So
584 // for such cases, we should install a slow path and never reach here. Fix
585 // it to not generate this for LoadGlobals.
586 CSA_ASSERT(this,
587 WordNotEqual(IntPtrConstant(static_cast<int>(on_nonexistent)),
588 IntPtrConstant(static_cast<int>(
589 OnNonExistent::kThrowReferenceError))));
590 TVARIABLE(IntPtrT, var_index);
591 TVARIABLE(Name, var_unique);
592
593 Label if_index(this), if_unique_name(this),
594 to_name_failed(this, Label::kDeferred);
595
596 if (support_elements == kSupportElements) {
597 DCHECK_NE(on_nonexistent, OnNonExistent::kThrowReferenceError);
598
599 TryToName(p->name(), &if_index, &var_index, &if_unique_name, &var_unique,
600 &to_name_failed);
601
602 BIND(&if_unique_name);
603 exit_point->ReturnCallStub(
604 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
605 p->context(), holder, var_unique.value(), p->receiver(),
606 SmiConstant(on_nonexistent));
607
608 BIND(&if_index);
609 // TODO(mslekova): introduce TryToName that doesn't try to compute
610 // the intptr index value
611 Goto(&to_name_failed);
612
613 BIND(&to_name_failed);
614 // TODO(duongn): use GetPropertyWithReceiver builtin once
615 // |lookup_element_in_holder| supports elements.
616 exit_point->ReturnCallRuntime(Runtime::kGetPropertyWithReceiver,
617 p->context(), holder, p->name(),
618 p->receiver(), SmiConstant(on_nonexistent));
619 } else {
620 exit_point->ReturnCallStub(
621 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
622 p->context(), holder, p->name(), p->receiver(),
623 SmiConstant(on_nonexistent));
624 }
625 }
626
627 BIND(&global);
628 {
629 CSA_ASSERT(this, IsPropertyCell(CAST(holder)));
630 // Ensure the property cell doesn't contain the hole.
631 TNode<Object> value =
632 LoadObjectField(CAST(holder), PropertyCell::kValueOffset);
633 TNode<Uint32T> details = Unsigned(LoadAndUntagToWord32ObjectField(
634 CAST(holder), PropertyCell::kPropertyDetailsRawOffset));
635 GotoIf(IsTheHole(value), miss);
636
637 exit_point->Return(CallGetterIfAccessor(value, CAST(holder), details,
638 p->context(), p->receiver(), miss));
639 }
640
641 BIND(&interceptor);
642 {
643 Comment("load_interceptor");
644 exit_point->ReturnCallRuntime(Runtime::kLoadPropertyWithInterceptor,
645 p->context(), p->name(), p->receiver(),
646 holder, p->slot(), p->vector());
647 }
648 BIND(&slow);
649 {
650 Comment("load_slow");
651 if (ic_mode == ICMode::kGlobalIC) {
652 exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Slow, p->context(),
653 p->name(), p->slot(), p->vector());
654
655 } else {
656 exit_point->ReturnCallRuntime(Runtime::kGetProperty, p->context(), holder,
657 p->name(), p->receiver());
658 }
659 }
660
661 BIND(&module_export);
662 {
663 Comment("module export");
664 TNode<UintPtrT> index =
665 DecodeWord<LoadHandler::ExportsIndexBits>(handler_word);
666 TNode<Module> module = LoadObjectField<Module>(
667 CAST(p->receiver()), JSModuleNamespace::kModuleOffset);
668 TNode<ObjectHashTable> exports =
669 LoadObjectField<ObjectHashTable>(module, Module::kExportsOffset);
670 TNode<Cell> cell = CAST(LoadFixedArrayElement(exports, index));
671 // The handler is only installed for exports that exist.
672 TNode<Object> value = LoadCellValue(cell);
673 Label is_the_hole(this, Label::kDeferred);
674 GotoIf(IsTheHole(value), &is_the_hole);
675 exit_point->Return(value);
676
677 BIND(&is_the_hole);
678 {
679 TNode<Smi> message = SmiConstant(MessageTemplate::kNotDefined);
680 exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context(),
681 message, p->name());
682 }
683 }
684
685 BIND(rebox_double);
686 exit_point->Return(AllocateHeapNumberWithValue(var_double_value->value()));
687 }
688
HandleLoadICSmiHandlerHasNamedCase(const LazyLoadICParameters * p,TNode<Object> holder,TNode<IntPtrT> handler_kind,Label * miss,ExitPoint * exit_point,ICMode ic_mode)689 void AccessorAssembler::HandleLoadICSmiHandlerHasNamedCase(
690 const LazyLoadICParameters* p, TNode<Object> holder,
691 TNode<IntPtrT> handler_kind, Label* miss, ExitPoint* exit_point,
692 ICMode ic_mode) {
693 Label return_true(this), return_false(this), return_lookup(this),
694 normal(this), global(this), slow(this);
695
696 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)),
697 &return_true);
698
699 GotoIf(WordEqual(handler_kind,
700 IntPtrConstant(LoadHandler::kConstantFromPrototype)),
701 &return_true);
702
703 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
704 &return_false);
705
706 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)),
707 &normal);
708
709 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
710 &return_true);
711
712 GotoIf(
713 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)),
714 &return_true);
715
716 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)),
717 &return_true);
718
719 GotoIf(WordEqual(handler_kind,
720 IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)),
721 &return_true);
722
723 GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kSlow)), &slow);
724
725 Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global,
726 &return_lookup);
727
728 BIND(&return_true);
729 exit_point->Return(TrueConstant());
730
731 BIND(&return_false);
732 exit_point->Return(FalseConstant());
733
734 BIND(&return_lookup);
735 {
736 CSA_ASSERT(
737 this,
738 Word32Or(
739 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kInterceptor)),
740 Word32Or(
741 WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)),
742 WordEqual(handler_kind,
743 IntPtrConstant(LoadHandler::kModuleExport)))));
744 exit_point->ReturnCallStub(
745 Builtins::CallableFor(isolate(), Builtins::kHasProperty), p->context(),
746 p->receiver(), p->name());
747 }
748
749 BIND(&normal);
750 {
751 Comment("has_normal");
752 TNode<NameDictionary> properties = CAST(LoadSlowProperties(CAST(holder)));
753 TVARIABLE(IntPtrT, var_name_index);
754 Label found(this);
755 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()), &found,
756 &var_name_index, miss);
757
758 BIND(&found);
759 exit_point->Return(TrueConstant());
760 }
761
762 BIND(&global);
763 {
764 CSA_ASSERT(this, IsPropertyCell(CAST(holder)));
765 // Ensure the property cell doesn't contain the hole.
766 TNode<Object> value =
767 LoadObjectField(CAST(holder), PropertyCell::kValueOffset);
768 GotoIf(IsTheHole(value), miss);
769
770 exit_point->Return(TrueConstant());
771 }
772
773 BIND(&slow);
774 {
775 Comment("load_slow");
776 if (ic_mode == ICMode::kGlobalIC) {
777 exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Slow, p->context(),
778 p->name(), p->slot(), p->vector());
779 } else {
780 exit_point->ReturnCallRuntime(Runtime::kHasProperty, p->context(),
781 p->receiver(), p->name());
782 }
783 }
784 }
785
786 // Performs actions common to both load and store handlers:
787 // 1. Checks prototype validity cell.
788 // 2. If |on_code_handler| is provided, then it checks if the sub handler is
789 // a smi or code and if it's a code then it calls |on_code_handler| to
790 // generate a code that handles Code handlers.
791 // If |on_code_handler| is not provided, then only smi sub handler are
792 // expected.
793 // 3. Does access check on lookup start object if
794 // ICHandler::DoAccessCheckOnLookupStartObjectBits bit is set in the smi
795 // handler.
796 // 4. Does dictionary lookup on receiver if
797 // ICHandler::LookupOnLookupStartObjectBits bit is set in the smi handler. If
798 // |on_found_on_lookup_start_object| is provided then it calls it to
799 // generate a code that handles the "found on receiver case" or just misses
800 // if the |on_found_on_lookup_start_object| is not provided.
801 // 5. Falls through in a case of a smi handler which is returned from this
802 // function (tagged!).
803 // TODO(ishell): Remove templatezation once we move common bits from
804 // Load/StoreHandler to the base class.
805 template <typename ICHandler, typename ICParameters>
HandleProtoHandler(const ICParameters * p,TNode<DataHandler> handler,const OnCodeHandler & on_code_handler,const OnFoundOnLookupStartObject & on_found_on_lookup_start_object,Label * miss,ICMode ic_mode)806 TNode<Object> AccessorAssembler::HandleProtoHandler(
807 const ICParameters* p, TNode<DataHandler> handler,
808 const OnCodeHandler& on_code_handler,
809 const OnFoundOnLookupStartObject& on_found_on_lookup_start_object,
810 Label* miss, ICMode ic_mode) {
811 //
812 // Check prototype validity cell.
813 //
814 {
815 TNode<Object> maybe_validity_cell =
816 LoadObjectField(handler, ICHandler::kValidityCellOffset);
817 CheckPrototypeValidityCell(maybe_validity_cell, miss);
818 }
819
820 //
821 // Check smi handler bits.
822 //
823 {
824 TNode<Object> smi_or_code_handler =
825 LoadObjectField(handler, ICHandler::kSmiHandlerOffset);
826 if (on_code_handler) {
827 Label if_smi_handler(this);
828 GotoIf(TaggedIsSmi(smi_or_code_handler), &if_smi_handler);
829
830 on_code_handler(CAST(smi_or_code_handler));
831
832 BIND(&if_smi_handler);
833 }
834 TNode<IntPtrT> handler_flags = SmiUntag(CAST(smi_or_code_handler));
835
836 // Lookup on receiver and access checks are not necessary for global ICs
837 // because in the former case the validity cell check guards modifications
838 // of the global object and the latter is not applicable to the global
839 // object.
840 int mask = ICHandler::LookupOnLookupStartObjectBits::kMask |
841 ICHandler::DoAccessCheckOnLookupStartObjectBits::kMask;
842 if (ic_mode == ICMode::kGlobalIC) {
843 CSA_ASSERT(this, IsClearWord(handler_flags, mask));
844 } else {
845 DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode);
846
847 Label done(this), if_do_access_check(this),
848 if_lookup_on_lookup_start_object(this);
849 GotoIf(IsClearWord(handler_flags, mask), &done);
850 // Only one of the bits can be set at a time.
851 CSA_ASSERT(this,
852 WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)),
853 IntPtrConstant(mask)));
854 Branch(
855 IsSetWord<typename ICHandler::DoAccessCheckOnLookupStartObjectBits>(
856 handler_flags),
857 &if_do_access_check, &if_lookup_on_lookup_start_object);
858
859 BIND(&if_do_access_check);
860 {
861 TNode<MaybeObject> data2 = LoadHandlerDataField(handler, 2);
862 CSA_ASSERT(this, IsWeakOrCleared(data2));
863 TNode<Context> expected_native_context =
864 CAST(GetHeapObjectAssumeWeak(data2, miss));
865 EmitAccessCheck(expected_native_context, p->context(),
866 p->lookup_start_object(), &done, miss);
867 }
868
869 BIND(&if_lookup_on_lookup_start_object);
870 {
871 // Dictionary lookup on lookup start object is not necessary for
872 // Load/StoreGlobalIC (which is the only case when the
873 // lookup_start_object can be a JSGlobalObject) because prototype
874 // validity cell check already guards modifications of the global
875 // object.
876 CSA_ASSERT(this,
877 Word32BinaryNot(HasInstanceType(
878 CAST(p->lookup_start_object()), JS_GLOBAL_OBJECT_TYPE)));
879
880 TNode<NameDictionary> properties =
881 CAST(LoadSlowProperties(CAST(p->lookup_start_object())));
882 TVARIABLE(IntPtrT, var_name_index);
883 Label found(this, &var_name_index);
884 NameDictionaryLookup<NameDictionary>(properties, CAST(p->name()),
885 &found, &var_name_index, &done);
886 BIND(&found);
887 {
888 if (on_found_on_lookup_start_object) {
889 on_found_on_lookup_start_object(properties, var_name_index.value());
890 } else {
891 Goto(miss);
892 }
893 }
894 }
895
896 BIND(&done);
897 }
898 return smi_or_code_handler;
899 }
900 }
901
HandleLoadICProtoHandler(const LazyLoadICParameters * p,TNode<DataHandler> handler,TVariable<Object> * var_holder,TVariable<Object> * var_smi_handler,Label * if_smi_handler,Label * miss,ExitPoint * exit_point,ICMode ic_mode,LoadAccessMode access_mode)902 void AccessorAssembler::HandleLoadICProtoHandler(
903 const LazyLoadICParameters* p, TNode<DataHandler> handler,
904 TVariable<Object>* var_holder, TVariable<Object>* var_smi_handler,
905 Label* if_smi_handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode,
906 LoadAccessMode access_mode) {
907 TNode<Smi> smi_handler = CAST(HandleProtoHandler<LoadHandler>(
908 p, handler,
909 // Code sub-handlers are not expected in LoadICs, so no |on_code_handler|.
910 nullptr,
911 // on_found_on_lookup_start_object
912 [=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
913 if (access_mode == LoadAccessMode::kHas) {
914 exit_point->Return(TrueConstant());
915 } else {
916 TVARIABLE(Uint32T, var_details);
917 TVARIABLE(Object, var_value);
918 LoadPropertyFromNameDictionary(properties, name_index, &var_details,
919 &var_value);
920 TNode<Object> value = CallGetterIfAccessor(
921 var_value.value(), CAST(var_holder->value()), var_details.value(),
922 p->context(), p->receiver(), miss);
923 exit_point->Return(value);
924 }
925 },
926 miss, ic_mode));
927
928 TNode<MaybeObject> maybe_holder_or_constant =
929 LoadHandlerDataField(handler, 1);
930
931 Label load_from_cached_holder(this), is_smi(this), done(this);
932
933 GotoIf(TaggedIsSmi(maybe_holder_or_constant), &is_smi);
934 Branch(TaggedEqual(maybe_holder_or_constant, NullConstant()), &done,
935 &load_from_cached_holder);
936
937 BIND(&is_smi);
938 {
939 CSA_ASSERT(
940 this,
941 WordEqual(
942 Signed(DecodeWord<LoadHandler::KindBits>(SmiUntag(smi_handler))),
943 IntPtrConstant(LoadHandler::kConstantFromPrototype)));
944 if (access_mode == LoadAccessMode::kHas) {
945 exit_point->Return(TrueConstant());
946 } else {
947 exit_point->Return(CAST(maybe_holder_or_constant));
948 }
949 }
950
951 BIND(&load_from_cached_holder);
952 {
953 // For regular holders, having passed the receiver map check and
954 // the validity cell check implies that |holder| is
955 // alive. However, for global object receivers, |maybe_holder| may
956 // be cleared.
957 CSA_ASSERT(this, IsWeakOrCleared(maybe_holder_or_constant));
958 TNode<HeapObject> holder =
959 GetHeapObjectAssumeWeak(maybe_holder_or_constant, miss);
960 *var_holder = holder;
961 Goto(&done);
962 }
963
964 BIND(&done);
965 {
966 *var_smi_handler = smi_handler;
967 Goto(if_smi_handler);
968 }
969 }
970
EmitAccessCheck(TNode<Context> expected_native_context,TNode<Context> context,TNode<Object> receiver,Label * can_access,Label * miss)971 void AccessorAssembler::EmitAccessCheck(TNode<Context> expected_native_context,
972 TNode<Context> context,
973 TNode<Object> receiver,
974 Label* can_access, Label* miss) {
975 CSA_ASSERT(this, IsNativeContext(expected_native_context));
976
977 TNode<NativeContext> native_context = LoadNativeContext(context);
978 GotoIf(TaggedEqual(expected_native_context, native_context), can_access);
979 // If the receiver is not a JSGlobalProxy then we miss.
980 GotoIfNot(IsJSGlobalProxy(CAST(receiver)), miss);
981 // For JSGlobalProxy receiver try to compare security tokens of current
982 // and expected native contexts.
983 TNode<Object> expected_token = LoadContextElement(
984 expected_native_context, Context::SECURITY_TOKEN_INDEX);
985 TNode<Object> current_token =
986 LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
987 Branch(TaggedEqual(expected_token, current_token), can_access, miss);
988 }
989
JumpIfDataProperty(TNode<Uint32T> details,Label * writable,Label * readonly)990 void AccessorAssembler::JumpIfDataProperty(TNode<Uint32T> details,
991 Label* writable, Label* readonly) {
992 if (readonly) {
993 // Accessor properties never have the READ_ONLY attribute set.
994 GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask),
995 readonly);
996 } else {
997 CSA_ASSERT(this, IsNotSetWord32(details,
998 PropertyDetails::kAttributesReadOnlyMask));
999 }
1000 TNode<Uint32T> kind = DecodeWord32<PropertyDetails::KindField>(details);
1001 GotoIf(Word32Equal(kind, Int32Constant(kData)), writable);
1002 // Fall through if it's an accessor property.
1003 }
1004
HandleStoreICNativeDataProperty(const StoreICParameters * p,TNode<HeapObject> holder,TNode<Word32T> handler_word)1005 void AccessorAssembler::HandleStoreICNativeDataProperty(
1006 const StoreICParameters* p, TNode<HeapObject> holder,
1007 TNode<Word32T> handler_word) {
1008 Comment("native_data_property_store");
1009 TNode<IntPtrT> descriptor =
1010 Signed(DecodeWordFromWord32<StoreHandler::DescriptorBits>(handler_word));
1011 TNode<AccessorInfo> accessor_info =
1012 CAST(LoadDescriptorValue(LoadMap(holder), descriptor));
1013
1014 TailCallRuntime(Runtime::kStoreCallbackProperty, p->context(), p->receiver(),
1015 holder, accessor_info, p->name(), p->value());
1016 }
1017
HandleStoreICHandlerCase(const StoreICParameters * p,TNode<MaybeObject> handler,Label * miss,ICMode ic_mode,ElementSupport support_elements)1018 void AccessorAssembler::HandleStoreICHandlerCase(
1019 const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss,
1020 ICMode ic_mode, ElementSupport support_elements) {
1021 Label if_smi_handler(this), if_nonsmi_handler(this);
1022 Label if_proto_handler(this), call_handler(this),
1023 store_transition_or_global(this);
1024
1025 Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler);
1026
1027 // |handler| is a Smi, encoding what to do. See SmiHandler methods
1028 // for the encoding format.
1029 BIND(&if_smi_handler);
1030 {
1031 TNode<Object> holder = p->receiver();
1032 TNode<Int32T> handler_word = SmiToInt32(CAST(handler));
1033
1034 Label if_fast_smi(this), if_proxy(this), if_interceptor(this),
1035 if_slow(this);
1036
1037 STATIC_ASSERT(StoreHandler::kGlobalProxy + 1 == StoreHandler::kNormal);
1038 STATIC_ASSERT(StoreHandler::kNormal + 1 == StoreHandler::kInterceptor);
1039 STATIC_ASSERT(StoreHandler::kInterceptor + 1 == StoreHandler::kSlow);
1040 STATIC_ASSERT(StoreHandler::kSlow + 1 == StoreHandler::kProxy);
1041 STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber);
1042
1043 TNode<Uint32T> handler_kind =
1044 DecodeWord32<StoreHandler::KindBits>(handler_word);
1045 GotoIf(
1046 Int32LessThan(handler_kind, Int32Constant(StoreHandler::kGlobalProxy)),
1047 &if_fast_smi);
1048 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kProxy)),
1049 &if_proxy);
1050 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kInterceptor)),
1051 &if_interceptor);
1052 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kSlow)),
1053 &if_slow);
1054 CSA_ASSERT(this,
1055 Word32Equal(handler_kind, Int32Constant(StoreHandler::kNormal)));
1056 TNode<NameDictionary> properties = CAST(LoadSlowProperties(CAST(holder)));
1057
1058 TVARIABLE(IntPtrT, var_name_index);
1059 Label dictionary_found(this, &var_name_index);
1060 NameDictionaryLookup<NameDictionary>(
1061 properties, CAST(p->name()), &dictionary_found, &var_name_index, miss);
1062 BIND(&dictionary_found);
1063 {
1064 TNode<Uint32T> details =
1065 LoadDetailsByKeyIndex(properties, var_name_index.value());
1066 // Check that the property is a writable data property (no accessor).
1067 const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask |
1068 PropertyDetails::kAttributesReadOnlyMask;
1069 STATIC_ASSERT(kData == 0);
1070 GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
1071
1072 StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(),
1073 p->value());
1074 Return(p->value());
1075 }
1076
1077 BIND(&if_fast_smi);
1078 {
1079 TNode<Uint32T> handler_kind =
1080 DecodeWord32<StoreHandler::KindBits>(handler_word);
1081
1082 Label data(this), accessor(this), native_data_property(this);
1083 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kAccessor)),
1084 &accessor);
1085 Branch(Word32Equal(handler_kind,
1086 Int32Constant(StoreHandler::kNativeDataProperty)),
1087 &native_data_property, &data);
1088
1089 BIND(&accessor);
1090 HandleStoreAccessor(p, CAST(holder), handler_word);
1091
1092 BIND(&native_data_property);
1093 HandleStoreICNativeDataProperty(p, CAST(holder), handler_word);
1094
1095 BIND(&data);
1096 // Handle non-transitioning field stores.
1097 HandleStoreICSmiHandlerCase(handler_word, CAST(holder), p->value(), miss);
1098 }
1099
1100 BIND(&if_proxy);
1101 HandleStoreToProxy(p, CAST(holder), miss, support_elements);
1102
1103 BIND(&if_interceptor);
1104 {
1105 Comment("store_interceptor");
1106 TailCallRuntime(Runtime::kStorePropertyWithInterceptor, p->context(),
1107 p->value(), p->receiver(), p->name());
1108 }
1109
1110 BIND(&if_slow);
1111 {
1112 Comment("store_slow");
1113 // The slow case calls into the runtime to complete the store without
1114 // causing an IC miss that would otherwise cause a transition to the
1115 // generic stub.
1116 if (ic_mode == ICMode::kGlobalIC) {
1117 TailCallRuntime(Runtime::kStoreGlobalIC_Slow, p->context(), p->value(),
1118 p->slot(), p->vector(), p->receiver(), p->name());
1119 } else {
1120 TailCallRuntime(Runtime::kKeyedStoreIC_Slow, p->context(), p->value(),
1121 p->receiver(), p->name());
1122 }
1123 }
1124 }
1125
1126 BIND(&if_nonsmi_handler);
1127 {
1128 GotoIf(IsWeakOrCleared(handler), &store_transition_or_global);
1129 TNode<HeapObject> strong_handler = CAST(handler);
1130 TNode<Map> handler_map = LoadMap(strong_handler);
1131 Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler);
1132
1133 BIND(&if_proto_handler);
1134 {
1135 HandleStoreICProtoHandler(p, CAST(strong_handler), miss, ic_mode,
1136 support_elements);
1137 }
1138
1139 // |handler| is a heap object. Must be code, call it.
1140 BIND(&call_handler);
1141 {
1142 TailCallStub(StoreWithVectorDescriptor{}, CAST(strong_handler),
1143 p->context(), p->receiver(), p->name(), p->value(),
1144 p->slot(), p->vector());
1145 }
1146 }
1147
1148 BIND(&store_transition_or_global);
1149 {
1150 // Load value or miss if the {handler} weak cell is cleared.
1151 CSA_ASSERT(this, IsWeakOrCleared(handler));
1152 TNode<HeapObject> map_or_property_cell =
1153 GetHeapObjectAssumeWeak(handler, miss);
1154
1155 Label store_global(this), store_transition(this);
1156 Branch(IsMap(map_or_property_cell), &store_transition, &store_global);
1157
1158 BIND(&store_global);
1159 {
1160 TNode<PropertyCell> property_cell = CAST(map_or_property_cell);
1161 ExitPoint direct_exit(this);
1162 StoreGlobalIC_PropertyCellCase(property_cell, p->value(), &direct_exit,
1163 miss);
1164 }
1165 BIND(&store_transition);
1166 {
1167 TNode<Map> map = CAST(map_or_property_cell);
1168 HandleStoreICTransitionMapHandlerCase(p, map, miss,
1169 kCheckPrototypeValidity);
1170 Return(p->value());
1171 }
1172 }
1173 }
1174
HandleStoreICTransitionMapHandlerCase(const StoreICParameters * p,TNode<Map> transition_map,Label * miss,StoreTransitionMapFlags flags)1175 void AccessorAssembler::HandleStoreICTransitionMapHandlerCase(
1176 const StoreICParameters* p, TNode<Map> transition_map, Label* miss,
1177 StoreTransitionMapFlags flags) {
1178 DCHECK_EQ(0, flags & ~kStoreTransitionMapFlagsMask);
1179 if (flags & kCheckPrototypeValidity) {
1180 TNode<Object> maybe_validity_cell =
1181 LoadObjectField(transition_map, Map::kPrototypeValidityCellOffset);
1182 CheckPrototypeValidityCell(maybe_validity_cell, miss);
1183 }
1184
1185 TNode<Uint32T> bitfield3 = LoadMapBitField3(transition_map);
1186 CSA_ASSERT(this, IsClearWord32<Map::Bits3::IsDictionaryMapBit>(bitfield3));
1187 GotoIf(IsSetWord32<Map::Bits3::IsDeprecatedBit>(bitfield3), miss);
1188
1189 // Load last descriptor details.
1190 TNode<UintPtrT> nof =
1191 DecodeWordFromWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bitfield3);
1192 CSA_ASSERT(this, WordNotEqual(nof, IntPtrConstant(0)));
1193 TNode<DescriptorArray> descriptors = LoadMapDescriptors(transition_map);
1194
1195 TNode<IntPtrT> factor = IntPtrConstant(DescriptorArray::kEntrySize);
1196 TNode<IntPtrT> last_key_index = UncheckedCast<IntPtrT>(IntPtrAdd(
1197 IntPtrConstant(DescriptorArray::ToKeyIndex(-1)), IntPtrMul(nof, factor)));
1198 if (flags & kValidateTransitionHandler) {
1199 TNode<Name> key = LoadKeyByKeyIndex(descriptors, last_key_index);
1200 GotoIf(TaggedNotEqual(key, p->name()), miss);
1201 } else {
1202 CSA_ASSERT(this, TaggedEqual(LoadKeyByKeyIndex(descriptors, last_key_index),
1203 p->name()));
1204 }
1205 TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, last_key_index);
1206 if (flags & kValidateTransitionHandler) {
1207 // Follow transitions only in the following cases:
1208 // 1) name is a non-private symbol and attributes equal to NONE,
1209 // 2) name is a private symbol and attributes equal to DONT_ENUM.
1210 Label attributes_ok(this);
1211 const int kKindAndAttributesDontDeleteReadOnlyMask =
1212 PropertyDetails::KindField::kMask |
1213 PropertyDetails::kAttributesDontDeleteMask |
1214 PropertyDetails::kAttributesReadOnlyMask;
1215 STATIC_ASSERT(kData == 0);
1216 // Both DontDelete and ReadOnly attributes must not be set and it has to be
1217 // a kData property.
1218 GotoIf(IsSetWord32(details, kKindAndAttributesDontDeleteReadOnlyMask),
1219 miss);
1220
1221 // DontEnum attribute is allowed only for private symbols and vice versa.
1222 Branch(Word32Equal(
1223 IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask),
1224 IsPrivateSymbol(CAST(p->name()))),
1225 &attributes_ok, miss);
1226
1227 BIND(&attributes_ok);
1228 }
1229
1230 OverwriteExistingFastDataProperty(CAST(p->receiver()), transition_map,
1231 descriptors, last_key_index, details,
1232 p->value(), miss, true);
1233 }
1234
CheckFieldType(TNode<DescriptorArray> descriptors,TNode<IntPtrT> name_index,TNode<Word32T> representation,TNode<Object> value,Label * bailout)1235 void AccessorAssembler::CheckFieldType(TNode<DescriptorArray> descriptors,
1236 TNode<IntPtrT> name_index,
1237 TNode<Word32T> representation,
1238 TNode<Object> value, Label* bailout) {
1239 Label r_smi(this), r_double(this), r_heapobject(this), all_fine(this);
1240 // Ignore FLAG_track_fields etc. and always emit code for all checks,
1241 // because this builtin is part of the snapshot and therefore should
1242 // be flag independent.
1243 GotoIf(Word32Equal(representation, Int32Constant(Representation::kSmi)),
1244 &r_smi);
1245 GotoIf(Word32Equal(representation, Int32Constant(Representation::kDouble)),
1246 &r_double);
1247 GotoIf(
1248 Word32Equal(representation, Int32Constant(Representation::kHeapObject)),
1249 &r_heapobject);
1250 GotoIf(Word32Equal(representation, Int32Constant(Representation::kNone)),
1251 bailout);
1252 CSA_ASSERT(this, Word32Equal(representation,
1253 Int32Constant(Representation::kTagged)));
1254 Goto(&all_fine);
1255
1256 BIND(&r_smi);
1257 { Branch(TaggedIsSmi(value), &all_fine, bailout); }
1258
1259 BIND(&r_double);
1260 {
1261 GotoIf(TaggedIsSmi(value), &all_fine);
1262 Branch(IsHeapNumber(CAST(value)), &all_fine, bailout);
1263 }
1264
1265 BIND(&r_heapobject);
1266 {
1267 GotoIf(TaggedIsSmi(value), bailout);
1268 TNode<MaybeObject> field_type =
1269 LoadFieldTypeByKeyIndex(descriptors, name_index);
1270 const Address kNoneType = FieldType::None().ptr();
1271 const Address kAnyType = FieldType::Any().ptr();
1272 DCHECK_NE(static_cast<uint32_t>(kNoneType), kClearedWeakHeapObjectLower32);
1273 DCHECK_NE(static_cast<uint32_t>(kAnyType), kClearedWeakHeapObjectLower32);
1274 // FieldType::None can't hold any value.
1275 GotoIf(
1276 TaggedEqual(field_type, BitcastWordToTagged(IntPtrConstant(kNoneType))),
1277 bailout);
1278 // FieldType::Any can hold any value.
1279 GotoIf(
1280 TaggedEqual(field_type, BitcastWordToTagged(IntPtrConstant(kAnyType))),
1281 &all_fine);
1282 // Cleared weak references count as FieldType::None, which can't hold any
1283 // value.
1284 TNode<Map> field_type_map =
1285 CAST(GetHeapObjectAssumeWeak(field_type, bailout));
1286 // FieldType::Class(...) performs a map check.
1287 Branch(TaggedEqual(LoadMap(CAST(value)), field_type_map), &all_fine,
1288 bailout);
1289 }
1290
1291 BIND(&all_fine);
1292 }
1293
IsPropertyDetailsConst(TNode<Uint32T> details)1294 TNode<BoolT> AccessorAssembler::IsPropertyDetailsConst(TNode<Uint32T> details) {
1295 return Word32Equal(DecodeWord32<PropertyDetails::ConstnessField>(details),
1296 Int32Constant(static_cast<int32_t>(VariableMode::kConst)));
1297 }
1298
OverwriteExistingFastDataProperty(TNode<HeapObject> object,TNode<Map> object_map,TNode<DescriptorArray> descriptors,TNode<IntPtrT> descriptor_name_index,TNode<Uint32T> details,TNode<Object> value,Label * slow,bool do_transitioning_store)1299 void AccessorAssembler::OverwriteExistingFastDataProperty(
1300 TNode<HeapObject> object, TNode<Map> object_map,
1301 TNode<DescriptorArray> descriptors, TNode<IntPtrT> descriptor_name_index,
1302 TNode<Uint32T> details, TNode<Object> value, Label* slow,
1303 bool do_transitioning_store) {
1304 Label done(this), if_field(this), if_descriptor(this);
1305
1306 CSA_ASSERT(this,
1307 Word32Equal(DecodeWord32<PropertyDetails::KindField>(details),
1308 Int32Constant(kData)));
1309
1310 Branch(Word32Equal(DecodeWord32<PropertyDetails::LocationField>(details),
1311 Int32Constant(kField)),
1312 &if_field, &if_descriptor);
1313
1314 BIND(&if_field);
1315 {
1316 TNode<Uint32T> representation =
1317 DecodeWord32<PropertyDetails::RepresentationField>(details);
1318
1319 CheckFieldType(descriptors, descriptor_name_index, representation, value,
1320 slow);
1321
1322 TNode<UintPtrT> field_index =
1323 DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details);
1324 field_index = Unsigned(
1325 IntPtrAdd(field_index,
1326 Unsigned(LoadMapInobjectPropertiesStartInWords(object_map))));
1327 TNode<IntPtrT> instance_size_in_words =
1328 LoadMapInstanceSizeInWords(object_map);
1329
1330 Label inobject(this), backing_store(this);
1331 Branch(UintPtrLessThan(field_index, instance_size_in_words), &inobject,
1332 &backing_store);
1333
1334 BIND(&inobject);
1335 {
1336 TNode<IntPtrT> field_offset = Signed(TimesTaggedSize(field_index));
1337 Label tagged_rep(this), double_rep(this);
1338 Branch(
1339 Word32Equal(representation, Int32Constant(Representation::kDouble)),
1340 &double_rep, &tagged_rep);
1341 BIND(&double_rep);
1342 {
1343 TNode<Float64T> double_value = ChangeNumberToFloat64(CAST(value));
1344 if (FLAG_unbox_double_fields) {
1345 if (do_transitioning_store) {
1346 StoreMap(object, object_map);
1347 } else {
1348 Label store_value(this);
1349 GotoIfNot(IsPropertyDetailsConst(details), &store_value);
1350 TNode<Float64T> current_value =
1351 LoadObjectField<Float64T>(object, field_offset);
1352 BranchIfSameNumberValue(current_value, double_value, &store_value,
1353 slow);
1354 BIND(&store_value);
1355 }
1356 StoreObjectFieldNoWriteBarrier(object, field_offset, double_value);
1357 } else {
1358 if (do_transitioning_store) {
1359 TNode<HeapNumber> heap_number =
1360 AllocateHeapNumberWithValue(double_value);
1361 StoreMap(object, object_map);
1362 StoreObjectField(object, field_offset, heap_number);
1363 } else {
1364 TNode<HeapNumber> heap_number =
1365 CAST(LoadObjectField(object, field_offset));
1366 Label store_value(this);
1367 GotoIfNot(IsPropertyDetailsConst(details), &store_value);
1368 TNode<Float64T> current_value = LoadHeapNumberValue(heap_number);
1369 BranchIfSameNumberValue(current_value, double_value, &store_value,
1370 slow);
1371 BIND(&store_value);
1372 StoreHeapNumberValue(heap_number, double_value);
1373 }
1374 }
1375 Goto(&done);
1376 }
1377
1378 BIND(&tagged_rep);
1379 {
1380 if (do_transitioning_store) {
1381 StoreMap(object, object_map);
1382 } else {
1383 Label if_mutable(this);
1384 GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
1385 TNode<Object> current_value = LoadObjectField(object, field_offset);
1386 BranchIfSameValue(current_value, value, &done, slow,
1387 SameValueMode::kNumbersOnly);
1388 BIND(&if_mutable);
1389 }
1390 StoreObjectField(object, field_offset, value);
1391 Goto(&done);
1392 }
1393 }
1394
1395 BIND(&backing_store);
1396 {
1397 TNode<IntPtrT> backing_store_index =
1398 Signed(IntPtrSub(field_index, instance_size_in_words));
1399
1400 if (do_transitioning_store) {
1401 // Allocate mutable heap number before extending properties backing
1402 // store to ensure that heap verifier will not see the heap in
1403 // inconsistent state.
1404 TVARIABLE(Object, var_value, value);
1405 {
1406 Label cont(this);
1407 GotoIf(Word32NotEqual(representation,
1408 Int32Constant(Representation::kDouble)),
1409 &cont);
1410 {
1411 TNode<Float64T> double_value = ChangeNumberToFloat64(CAST(value));
1412 TNode<HeapNumber> heap_number =
1413 AllocateHeapNumberWithValue(double_value);
1414 var_value = heap_number;
1415 Goto(&cont);
1416 }
1417 BIND(&cont);
1418 }
1419
1420 TNode<PropertyArray> properties =
1421 ExtendPropertiesBackingStore(object, backing_store_index);
1422 StorePropertyArrayElement(properties, backing_store_index,
1423 var_value.value());
1424 StoreMap(object, object_map);
1425 Goto(&done);
1426
1427 } else {
1428 Label tagged_rep(this), double_rep(this);
1429 TNode<PropertyArray> properties =
1430 CAST(LoadFastProperties(CAST(object)));
1431 Branch(
1432 Word32Equal(representation, Int32Constant(Representation::kDouble)),
1433 &double_rep, &tagged_rep);
1434 BIND(&double_rep);
1435 {
1436 TNode<HeapNumber> heap_number =
1437 CAST(LoadPropertyArrayElement(properties, backing_store_index));
1438 TNode<Float64T> double_value = ChangeNumberToFloat64(CAST(value));
1439
1440 Label if_mutable(this);
1441 GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
1442 TNode<Float64T> current_value = LoadHeapNumberValue(heap_number);
1443 BranchIfSameNumberValue(current_value, double_value, &done, slow);
1444
1445 BIND(&if_mutable);
1446 StoreHeapNumberValue(heap_number, double_value);
1447 Goto(&done);
1448 }
1449 BIND(&tagged_rep);
1450 {
1451 Label if_mutable(this);
1452 GotoIfNot(IsPropertyDetailsConst(details), &if_mutable);
1453 TNode<Object> current_value =
1454 LoadPropertyArrayElement(properties, backing_store_index);
1455 BranchIfSameValue(current_value, value, &done, slow,
1456 SameValueMode::kNumbersOnly);
1457
1458 BIND(&if_mutable);
1459 StorePropertyArrayElement(properties, backing_store_index, value);
1460 Goto(&done);
1461 }
1462 }
1463 }
1464 }
1465
1466 BIND(&if_descriptor);
1467 {
1468 // Check that constant matches value.
1469 TNode<Object> constant =
1470 LoadValueByKeyIndex(descriptors, descriptor_name_index);
1471 GotoIf(TaggedNotEqual(value, constant), slow);
1472
1473 if (do_transitioning_store) {
1474 StoreMap(object, object_map);
1475 }
1476 Goto(&done);
1477 }
1478 BIND(&done);
1479 }
1480
CheckPrototypeValidityCell(TNode<Object> maybe_validity_cell,Label * miss)1481 void AccessorAssembler::CheckPrototypeValidityCell(
1482 TNode<Object> maybe_validity_cell, Label* miss) {
1483 Label done(this);
1484 GotoIf(
1485 TaggedEqual(maybe_validity_cell, SmiConstant(Map::kPrototypeChainValid)),
1486 &done);
1487 CSA_ASSERT(this, TaggedIsNotSmi(maybe_validity_cell));
1488
1489 TNode<Object> cell_value =
1490 LoadObjectField(CAST(maybe_validity_cell), Cell::kValueOffset);
1491 Branch(TaggedEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)), &done,
1492 miss);
1493
1494 BIND(&done);
1495 }
1496
HandleStoreAccessor(const StoreICParameters * p,TNode<HeapObject> holder,TNode<Word32T> handler_word)1497 void AccessorAssembler::HandleStoreAccessor(const StoreICParameters* p,
1498 TNode<HeapObject> holder,
1499 TNode<Word32T> handler_word) {
1500 Comment("accessor_store");
1501 TNode<IntPtrT> descriptor =
1502 Signed(DecodeWordFromWord32<StoreHandler::DescriptorBits>(handler_word));
1503 TNode<HeapObject> accessor_pair =
1504 CAST(LoadDescriptorValue(LoadMap(holder), descriptor));
1505 CSA_ASSERT(this, IsAccessorPair(accessor_pair));
1506 TNode<Object> setter =
1507 LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
1508 CSA_ASSERT(this, Word32BinaryNot(IsTheHole(setter)));
1509
1510 Callable callable = CodeFactory::Call(isolate());
1511 Return(Call(p->context(), setter, p->receiver(), p->value()));
1512 }
1513
HandleStoreICProtoHandler(const StoreICParameters * p,TNode<StoreHandler> handler,Label * miss,ICMode ic_mode,ElementSupport support_elements)1514 void AccessorAssembler::HandleStoreICProtoHandler(
1515 const StoreICParameters* p, TNode<StoreHandler> handler, Label* miss,
1516 ICMode ic_mode, ElementSupport support_elements) {
1517 Comment("HandleStoreICProtoHandler");
1518
1519 OnCodeHandler on_code_handler;
1520 if (support_elements == kSupportElements) {
1521 // Code sub-handlers are expected only in KeyedStoreICs.
1522 on_code_handler = [=](TNode<Code> code_handler) {
1523 // This is either element store or transitioning element store.
1524 Label if_element_store(this), if_transitioning_element_store(this);
1525 Branch(IsStoreHandler0Map(LoadMap(handler)), &if_element_store,
1526 &if_transitioning_element_store);
1527 BIND(&if_element_store);
1528 {
1529 TailCallStub(StoreWithVectorDescriptor{}, code_handler, p->context(),
1530 p->receiver(), p->name(), p->value(), p->slot(),
1531 p->vector());
1532 }
1533
1534 BIND(&if_transitioning_element_store);
1535 {
1536 TNode<MaybeObject> maybe_transition_map =
1537 LoadHandlerDataField(handler, 1);
1538 TNode<Map> transition_map =
1539 CAST(GetHeapObjectAssumeWeak(maybe_transition_map, miss));
1540
1541 GotoIf(IsDeprecatedMap(transition_map), miss);
1542
1543 TailCallStub(StoreTransitionDescriptor{}, code_handler, p->context(),
1544 p->receiver(), p->name(), transition_map, p->value(),
1545 p->slot(), p->vector());
1546 }
1547 };
1548 }
1549
1550 TNode<Object> smi_handler = HandleProtoHandler<StoreHandler>(
1551 p, handler, on_code_handler,
1552 // on_found_on_lookup_start_object
1553 [=](TNode<NameDictionary> properties, TNode<IntPtrT> name_index) {
1554 TNode<Uint32T> details = LoadDetailsByKeyIndex(properties, name_index);
1555 // Check that the property is a writable data property (no accessor).
1556 const int kTypeAndReadOnlyMask =
1557 PropertyDetails::KindField::kMask |
1558 PropertyDetails::kAttributesReadOnlyMask;
1559 STATIC_ASSERT(kData == 0);
1560 GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss);
1561
1562 StoreValueByKeyIndex<NameDictionary>(properties, name_index,
1563 p->value());
1564 Return(p->value());
1565 },
1566 miss, ic_mode);
1567
1568 {
1569 Label if_add_normal(this), if_store_global_proxy(this), if_api_setter(this),
1570 if_accessor(this), if_native_data_property(this), if_slow(this);
1571
1572 CSA_ASSERT(this, TaggedIsSmi(smi_handler));
1573 TNode<Int32T> handler_word = SmiToInt32(CAST(smi_handler));
1574
1575 TNode<Uint32T> handler_kind =
1576 DecodeWord32<StoreHandler::KindBits>(handler_word);
1577 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kNormal)),
1578 &if_add_normal);
1579
1580 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kSlow)),
1581 &if_slow);
1582
1583 TNode<MaybeObject> maybe_holder = LoadHandlerDataField(handler, 1);
1584 CSA_ASSERT(this, IsWeakOrCleared(maybe_holder));
1585 TNode<HeapObject> holder = GetHeapObjectAssumeWeak(maybe_holder, miss);
1586
1587 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kGlobalProxy)),
1588 &if_store_global_proxy);
1589
1590 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kAccessor)),
1591 &if_accessor);
1592
1593 GotoIf(Word32Equal(handler_kind,
1594 Int32Constant(StoreHandler::kNativeDataProperty)),
1595 &if_native_data_property);
1596
1597 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kApiSetter)),
1598 &if_api_setter);
1599
1600 GotoIf(
1601 Word32Equal(handler_kind,
1602 Int32Constant(StoreHandler::kApiSetterHolderIsPrototype)),
1603 &if_api_setter);
1604
1605 CSA_ASSERT(this,
1606 Word32Equal(handler_kind, Int32Constant(StoreHandler::kProxy)));
1607 HandleStoreToProxy(p, CAST(holder), miss, support_elements);
1608
1609 BIND(&if_slow);
1610 {
1611 Comment("store_slow");
1612 // The slow case calls into the runtime to complete the store without
1613 // causing an IC miss that would otherwise cause a transition to the
1614 // generic stub.
1615 if (ic_mode == ICMode::kGlobalIC) {
1616 TailCallRuntime(Runtime::kStoreGlobalIC_Slow, p->context(), p->value(),
1617 p->slot(), p->vector(), p->receiver(), p->name());
1618 } else {
1619 TailCallRuntime(Runtime::kKeyedStoreIC_Slow, p->context(), p->value(),
1620 p->receiver(), p->name());
1621 }
1622 }
1623
1624 BIND(&if_add_normal);
1625 {
1626 // This is a case of "transitioning store" to a dictionary mode object
1627 // when the property does not exist. The "existing property" case is
1628 // covered above by LookupOnLookupStartObject bit handling of the smi
1629 // handler.
1630 Label slow(this);
1631 TNode<Map> receiver_map = LoadMap(CAST(p->receiver()));
1632 InvalidateValidityCellIfPrototype(receiver_map);
1633
1634 TNode<NameDictionary> properties =
1635 CAST(LoadSlowProperties(CAST(p->receiver())));
1636 Add<NameDictionary>(properties, CAST(p->name()), p->value(), &slow);
1637 Return(p->value());
1638
1639 BIND(&slow);
1640 TailCallRuntime(Runtime::kAddDictionaryProperty, p->context(),
1641 p->receiver(), p->name(), p->value());
1642 }
1643
1644 BIND(&if_accessor);
1645 HandleStoreAccessor(p, holder, handler_word);
1646
1647 BIND(&if_native_data_property);
1648 HandleStoreICNativeDataProperty(p, holder, handler_word);
1649
1650 BIND(&if_api_setter);
1651 {
1652 Comment("api_setter");
1653 CSA_ASSERT(this, TaggedIsNotSmi(handler));
1654 TNode<CallHandlerInfo> call_handler_info = CAST(holder);
1655
1656 // Context is stored either in data2 or data3 field depending on whether
1657 // the access check is enabled for this handler or not.
1658 TNode<MaybeObject> maybe_context = Select<MaybeObject>(
1659 IsSetWord32<StoreHandler::DoAccessCheckOnLookupStartObjectBits>(
1660 handler_word),
1661 [=] { return LoadHandlerDataField(handler, 3); },
1662 [=] { return LoadHandlerDataField(handler, 2); });
1663
1664 CSA_ASSERT(this, IsWeakOrCleared(maybe_context));
1665 TNode<Object> context = Select<Object>(
1666 IsCleared(maybe_context), [=] { return SmiConstant(0); },
1667 [=] { return GetHeapObjectAssumeWeak(maybe_context); });
1668
1669 TNode<Foreign> foreign = LoadObjectField<Foreign>(
1670 call_handler_info, CallHandlerInfo::kJsCallbackOffset);
1671 TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
1672 TNode<Object> data =
1673 LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset);
1674
1675 TVARIABLE(Object, api_holder, p->receiver());
1676 Label store(this);
1677 GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kApiSetter)),
1678 &store);
1679
1680 CSA_ASSERT(this,
1681 Word32Equal(
1682 handler_kind,
1683 Int32Constant(StoreHandler::kApiSetterHolderIsPrototype)));
1684
1685 api_holder = LoadMapPrototype(LoadMap(CAST(p->receiver())));
1686 Goto(&store);
1687
1688 BIND(&store);
1689 TNode<IntPtrT> argc = IntPtrConstant(1);
1690 Return(CallApiCallback(context, callback, argc, data, api_holder.value(),
1691 p->receiver(), p->value()));
1692 }
1693
1694 BIND(&if_store_global_proxy);
1695 {
1696 ExitPoint direct_exit(this);
1697 StoreGlobalIC_PropertyCellCase(CAST(holder), p->value(), &direct_exit,
1698 miss);
1699 }
1700 }
1701 }
1702
HandleStoreToProxy(const StoreICParameters * p,TNode<JSProxy> proxy,Label * miss,ElementSupport support_elements)1703 void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p,
1704 TNode<JSProxy> proxy, Label* miss,
1705 ElementSupport support_elements) {
1706 TVARIABLE(IntPtrT, var_index);
1707 TVARIABLE(Name, var_unique);
1708
1709 Label if_index(this), if_unique_name(this),
1710 to_name_failed(this, Label::kDeferred);
1711
1712 if (support_elements == kSupportElements) {
1713 TryToName(p->name(), &if_index, &var_index, &if_unique_name, &var_unique,
1714 &to_name_failed);
1715
1716 BIND(&if_unique_name);
1717 CallBuiltin(Builtins::kProxySetProperty, p->context(), proxy,
1718 var_unique.value(), p->value(), p->receiver());
1719 Return(p->value());
1720
1721 // The index case is handled earlier by the runtime.
1722 BIND(&if_index);
1723 // TODO(mslekova): introduce TryToName that doesn't try to compute
1724 // the intptr index value
1725 Goto(&to_name_failed);
1726
1727 BIND(&to_name_failed);
1728 TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context(), proxy,
1729 p->name(), p->value(), p->receiver());
1730 } else {
1731 TNode<Object> name =
1732 CallBuiltin(Builtins::kToName, p->context(), p->name());
1733 TailCallBuiltin(Builtins::kProxySetProperty, p->context(), proxy, name,
1734 p->value(), p->receiver());
1735 }
1736 }
1737
HandleStoreICSmiHandlerCase(TNode<Word32T> handler_word,TNode<JSObject> holder,TNode<Object> value,Label * miss)1738 void AccessorAssembler::HandleStoreICSmiHandlerCase(TNode<Word32T> handler_word,
1739 TNode<JSObject> holder,
1740 TNode<Object> value,
1741 Label* miss) {
1742 Comment("field store");
1743 #ifdef DEBUG
1744 TNode<Uint32T> handler_kind =
1745 DecodeWord32<StoreHandler::KindBits>(handler_word);
1746 CSA_ASSERT(
1747 this,
1748 Word32Or(
1749 Word32Equal(handler_kind, Int32Constant(StoreHandler::kField)),
1750 Word32Equal(handler_kind, Int32Constant(StoreHandler::kConstField))));
1751 #endif
1752
1753 TNode<Uint32T> field_representation =
1754 DecodeWord32<StoreHandler::RepresentationBits>(handler_word);
1755
1756 Label if_smi_field(this), if_double_field(this), if_heap_object_field(this),
1757 if_tagged_field(this);
1758
1759 int32_t case_values[] = {Representation::kTagged, Representation::kHeapObject,
1760 Representation::kSmi};
1761 Label* case_labels[] = {&if_tagged_field, &if_heap_object_field,
1762 &if_smi_field};
1763
1764 Switch(field_representation, &if_double_field, case_values, case_labels, 3);
1765
1766 BIND(&if_tagged_field);
1767 {
1768 Comment("store tagged field");
1769 HandleStoreFieldAndReturn(handler_word, holder, value, base::nullopt,
1770 Representation::Tagged(), miss);
1771 }
1772
1773 BIND(&if_heap_object_field);
1774 {
1775 Comment("heap object field checks");
1776 CheckHeapObjectTypeMatchesDescriptor(handler_word, holder, value, miss);
1777
1778 Comment("store heap object field");
1779 HandleStoreFieldAndReturn(handler_word, holder, value, base::nullopt,
1780 Representation::HeapObject(), miss);
1781 }
1782
1783 BIND(&if_smi_field);
1784 {
1785 Comment("smi field checks");
1786 GotoIfNot(TaggedIsSmi(value), miss);
1787
1788 Comment("store smi field");
1789 HandleStoreFieldAndReturn(handler_word, holder, value, base::nullopt,
1790 Representation::Smi(), miss);
1791 }
1792
1793 BIND(&if_double_field);
1794 {
1795 CSA_ASSERT(this, Word32Equal(field_representation,
1796 Int32Constant(Representation::kDouble)));
1797 Comment("double field checks");
1798 TNode<Float64T> double_value = TryTaggedToFloat64(value, miss);
1799 CheckDescriptorConsidersNumbersMutable(handler_word, holder, miss);
1800
1801 Comment("store double field");
1802 HandleStoreFieldAndReturn(handler_word, holder, value, double_value,
1803 Representation::Double(), miss);
1804 }
1805 }
1806
CheckHeapObjectTypeMatchesDescriptor(TNode<Word32T> handler_word,TNode<JSObject> holder,TNode<Object> value,Label * bailout)1807 void AccessorAssembler::CheckHeapObjectTypeMatchesDescriptor(
1808 TNode<Word32T> handler_word, TNode<JSObject> holder, TNode<Object> value,
1809 Label* bailout) {
1810 GotoIf(TaggedIsSmi(value), bailout);
1811
1812 Label done(this);
1813 // Skip field type check in favor of constant value check when storing
1814 // to constant field.
1815 GotoIf(Word32Equal(DecodeWord32<StoreHandler::KindBits>(handler_word),
1816 Int32Constant(StoreHandler::kConstField)),
1817 &done);
1818 TNode<IntPtrT> descriptor =
1819 Signed(DecodeWordFromWord32<StoreHandler::DescriptorBits>(handler_word));
1820 TNode<MaybeObject> maybe_field_type =
1821 LoadDescriptorValueOrFieldType(LoadMap(holder), descriptor);
1822
1823 GotoIf(TaggedIsSmi(maybe_field_type), &done);
1824 // Check that value type matches the field type.
1825 {
1826 TNode<HeapObject> field_type =
1827 GetHeapObjectAssumeWeak(maybe_field_type, bailout);
1828 Branch(TaggedEqual(LoadMap(CAST(value)), field_type), &done, bailout);
1829 }
1830 BIND(&done);
1831 }
1832
CheckDescriptorConsidersNumbersMutable(TNode<Word32T> handler_word,TNode<JSObject> holder,Label * bailout)1833 void AccessorAssembler::CheckDescriptorConsidersNumbersMutable(
1834 TNode<Word32T> handler_word, TNode<JSObject> holder, Label* bailout) {
1835 // We have to check that the representation is Double. Checking the value
1836 // (either in the field or being assigned) is not enough, as we could have
1837 // transitioned to Tagged but still be holding a HeapNumber, which would no
1838 // longer be allowed to be mutable.
1839
1840 // TODO(leszeks): We could skip the representation check in favor of a
1841 // constant value check in HandleStoreFieldAndReturn here, but then
1842 // HandleStoreFieldAndReturn would need an IsHeapNumber check in case both the
1843 // representation changed and the value is no longer a HeapNumber.
1844 TNode<IntPtrT> descriptor_entry =
1845 Signed(DecodeWordFromWord32<StoreHandler::DescriptorBits>(handler_word));
1846 TNode<DescriptorArray> descriptors = LoadMapDescriptors(LoadMap(holder));
1847 TNode<Uint32T> details =
1848 LoadDetailsByDescriptorEntry(descriptors, descriptor_entry);
1849
1850 GotoIfNot(IsEqualInWord32<PropertyDetails::RepresentationField>(
1851 details, Representation::kDouble),
1852 bailout);
1853 }
1854
HandleStoreFieldAndReturn(TNode<Word32T> handler_word,TNode<JSObject> holder,TNode<Object> value,base::Optional<TNode<Float64T>> double_value,Representation representation,Label * miss)1855 void AccessorAssembler::HandleStoreFieldAndReturn(
1856 TNode<Word32T> handler_word, TNode<JSObject> holder, TNode<Object> value,
1857 base::Optional<TNode<Float64T>> double_value, Representation representation,
1858 Label* miss) {
1859 Label done(this);
1860
1861 bool store_value_as_double = representation.IsDouble();
1862
1863 TNode<BoolT> is_inobject =
1864 IsSetWord32<StoreHandler::IsInobjectBits>(handler_word);
1865 TNode<HeapObject> property_storage = Select<HeapObject>(
1866 is_inobject, [&]() { return holder; },
1867 [&]() { return LoadFastProperties(holder); });
1868
1869 TNode<UintPtrT> index =
1870 DecodeWordFromWord32<StoreHandler::FieldIndexBits>(handler_word);
1871 TNode<IntPtrT> offset = Signed(TimesTaggedSize(index));
1872
1873 // For Double fields, we want to mutate the current double-value
1874 // field rather than changing it to point at a new HeapNumber.
1875 if (store_value_as_double) {
1876 TVARIABLE(HeapObject, actual_property_storage, property_storage);
1877 TVARIABLE(IntPtrT, actual_offset, offset);
1878
1879 Label property_and_offset_ready(this);
1880
1881 // If we are unboxing double fields, and this is an in-object field, the
1882 // property_storage and offset are already pointing to the double-valued
1883 // field.
1884 if (FLAG_unbox_double_fields) {
1885 GotoIf(is_inobject, &property_and_offset_ready);
1886 }
1887
1888 // Store the double value directly into the mutable HeapNumber.
1889 TNode<Object> field = LoadObjectField(property_storage, offset);
1890 CSA_ASSERT(this, IsHeapNumber(CAST(field)));
1891 actual_property_storage = CAST(field);
1892 actual_offset = IntPtrConstant(HeapNumber::kValueOffset);
1893 Goto(&property_and_offset_ready);
1894
1895 BIND(&property_and_offset_ready);
1896 property_storage = actual_property_storage.value();
1897 offset = actual_offset.value();
1898 }
1899
1900 // Do constant value check if necessary.
1901 Label do_store(this);
1902 GotoIfNot(Word32Equal(DecodeWord32<StoreHandler::KindBits>(handler_word),
1903 Int32Constant(StoreHandler::kConstField)),
1904 &do_store);
1905 {
1906 if (store_value_as_double) {
1907 Label done(this);
1908 TNode<Float64T> current_value =
1909 LoadObjectField<Float64T>(property_storage, offset);
1910 BranchIfSameNumberValue(current_value, *double_value, &done, miss);
1911 BIND(&done);
1912 Return(value);
1913 } else {
1914 TNode<Object> current_value = LoadObjectField(property_storage, offset);
1915 GotoIfNot(TaggedEqual(current_value, value), miss);
1916 Return(value);
1917 }
1918 }
1919
1920 BIND(&do_store);
1921 // Do the store.
1922 if (store_value_as_double) {
1923 StoreObjectFieldNoWriteBarrier(property_storage, offset, *double_value);
1924 } else if (representation.IsSmi()) {
1925 TNode<Smi> value_smi = CAST(value);
1926 StoreObjectFieldNoWriteBarrier(property_storage, offset, value_smi);
1927 } else {
1928 StoreObjectField(property_storage, offset, value);
1929 }
1930
1931 Return(value);
1932 }
1933
ExtendPropertiesBackingStore(TNode<HeapObject> object,TNode<IntPtrT> index)1934 TNode<PropertyArray> AccessorAssembler::ExtendPropertiesBackingStore(
1935 TNode<HeapObject> object, TNode<IntPtrT> index) {
1936 Comment("[ Extend storage");
1937
1938 TVARIABLE(HeapObject, var_properties);
1939 TVARIABLE(Int32T, var_encoded_hash);
1940 TVARIABLE(IntPtrT, var_length);
1941
1942 TNode<Object> properties =
1943 LoadObjectField(object, JSObject::kPropertiesOrHashOffset);
1944
1945 Label if_smi_hash(this), if_property_array(this), extend_store(this);
1946 Branch(TaggedIsSmi(properties), &if_smi_hash, &if_property_array);
1947
1948 BIND(&if_smi_hash);
1949 {
1950 TNode<Int32T> hash = SmiToInt32(CAST(properties));
1951 TNode<Int32T> encoded_hash =
1952 Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift));
1953 var_encoded_hash = encoded_hash;
1954 var_length = IntPtrConstant(0);
1955 var_properties = EmptyFixedArrayConstant();
1956 Goto(&extend_store);
1957 }
1958
1959 BIND(&if_property_array);
1960 {
1961 var_properties = CAST(properties);
1962 TNode<Int32T> length_and_hash_int32 = LoadAndUntagToWord32ObjectField(
1963 var_properties.value(), PropertyArray::kLengthAndHashOffset);
1964 var_encoded_hash = Word32And(
1965 length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask));
1966 var_length = ChangeInt32ToIntPtr(
1967 Word32And(length_and_hash_int32,
1968 Int32Constant(PropertyArray::LengthField::kMask)));
1969 Goto(&extend_store);
1970 }
1971
1972 BIND(&extend_store);
1973 {
1974 TVARIABLE(HeapObject, var_new_properties, var_properties.value());
1975 Label done(this);
1976 // Previous property deletion could have left behind unused backing store
1977 // capacity even for a map that think it doesn't have any unused fields.
1978 // Perform a bounds check to see if we actually have to grow the array.
1979 GotoIf(UintPtrLessThan(index, ParameterToIntPtr(var_length.value())),
1980 &done);
1981
1982 TNode<IntPtrT> delta = IntPtrConstant(JSObject::kFieldsAdded);
1983 TNode<IntPtrT> new_capacity = IntPtrAdd(var_length.value(), delta);
1984
1985 // Grow properties array.
1986 DCHECK(kMaxNumberOfDescriptors + JSObject::kFieldsAdded <
1987 FixedArrayBase::GetMaxLengthForNewSpaceAllocation(PACKED_ELEMENTS));
1988 // The size of a new properties backing store is guaranteed to be small
1989 // enough that the new backing store will be allocated in new space.
1990 CSA_ASSERT(this, IntPtrLessThan(new_capacity,
1991 IntPtrConstant(kMaxNumberOfDescriptors +
1992 JSObject::kFieldsAdded)));
1993
1994 TNode<PropertyArray> new_properties = AllocatePropertyArray(new_capacity);
1995 var_new_properties = new_properties;
1996
1997 FillPropertyArrayWithUndefined(new_properties, var_length.value(),
1998 new_capacity);
1999
2000 // |new_properties| is guaranteed to be in new space, so we can skip
2001 // the write barrier.
2002 CopyPropertyArrayValues(var_properties.value(), new_properties,
2003 var_length.value(), SKIP_WRITE_BARRIER,
2004 DestroySource::kYes);
2005
2006 TNode<Int32T> new_capacity_int32 = TruncateIntPtrToInt32(new_capacity);
2007 TNode<Int32T> new_length_and_hash_int32 =
2008 Word32Or(var_encoded_hash.value(), new_capacity_int32);
2009 StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset,
2010 SmiFromInt32(new_length_and_hash_int32));
2011 StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties);
2012 Comment("] Extend storage");
2013 Goto(&done);
2014 BIND(&done);
2015 return CAST(var_new_properties.value());
2016 }
2017 }
2018
EmitFastElementsBoundsCheck(TNode<JSObject> object,TNode<FixedArrayBase> elements,TNode<IntPtrT> intptr_index,TNode<BoolT> is_jsarray_condition,Label * miss)2019 void AccessorAssembler::EmitFastElementsBoundsCheck(
2020 TNode<JSObject> object, TNode<FixedArrayBase> elements,
2021 TNode<IntPtrT> intptr_index, TNode<BoolT> is_jsarray_condition,
2022 Label* miss) {
2023 TVARIABLE(IntPtrT, var_length);
2024 Comment("Fast elements bounds check");
2025 Label if_array(this), length_loaded(this, &var_length);
2026 GotoIf(is_jsarray_condition, &if_array);
2027 {
2028 var_length = SmiUntag(LoadFixedArrayBaseLength(elements));
2029 Goto(&length_loaded);
2030 }
2031 BIND(&if_array);
2032 {
2033 var_length = SmiUntag(LoadFastJSArrayLength(CAST(object)));
2034 Goto(&length_loaded);
2035 }
2036 BIND(&length_loaded);
2037 GotoIfNot(UintPtrLessThan(intptr_index, var_length.value()), miss);
2038 }
2039
EmitElementLoad(TNode<HeapObject> object,TNode<Word32T> elements_kind,TNode<IntPtrT> intptr_index,TNode<BoolT> is_jsarray_condition,Label * if_hole,Label * rebox_double,TVariable<Float64T> * var_double_value,Label * unimplemented_elements_kind,Label * out_of_bounds,Label * miss,ExitPoint * exit_point,LoadAccessMode access_mode)2040 void AccessorAssembler::EmitElementLoad(
2041 TNode<HeapObject> object, TNode<Word32T> elements_kind,
2042 TNode<IntPtrT> intptr_index, TNode<BoolT> is_jsarray_condition,
2043 Label* if_hole, Label* rebox_double, TVariable<Float64T>* var_double_value,
2044 Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss,
2045 ExitPoint* exit_point, LoadAccessMode access_mode) {
2046 Label if_typed_array(this), if_fast(this), if_fast_packed(this),
2047 if_fast_holey(this), if_fast_double(this), if_fast_holey_double(this),
2048 if_nonfast(this), if_dictionary(this);
2049 Branch(Int32GreaterThan(elements_kind,
2050 Int32Constant(LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND)),
2051 &if_nonfast, &if_fast);
2052
2053 BIND(&if_fast);
2054 {
2055 TNode<FixedArrayBase> elements = LoadJSObjectElements(CAST(object));
2056 EmitFastElementsBoundsCheck(CAST(object), elements, intptr_index,
2057 is_jsarray_condition, out_of_bounds);
2058 int32_t kinds[] = {
2059 // Handled by if_fast_packed.
2060 PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, PACKED_NONEXTENSIBLE_ELEMENTS,
2061 PACKED_SEALED_ELEMENTS, PACKED_FROZEN_ELEMENTS,
2062 // Handled by if_fast_holey.
2063 HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS, HOLEY_NONEXTENSIBLE_ELEMENTS,
2064 HOLEY_FROZEN_ELEMENTS, HOLEY_SEALED_ELEMENTS,
2065 // Handled by if_fast_double.
2066 PACKED_DOUBLE_ELEMENTS,
2067 // Handled by if_fast_holey_double.
2068 HOLEY_DOUBLE_ELEMENTS};
2069 Label* labels[] = {// FAST_{SMI,}_ELEMENTS
2070 &if_fast_packed, &if_fast_packed, &if_fast_packed,
2071 &if_fast_packed, &if_fast_packed,
2072 // FAST_HOLEY_{SMI,}_ELEMENTS
2073 &if_fast_holey, &if_fast_holey, &if_fast_holey,
2074 &if_fast_holey, &if_fast_holey,
2075 // PACKED_DOUBLE_ELEMENTS
2076 &if_fast_double,
2077 // HOLEY_DOUBLE_ELEMENTS
2078 &if_fast_holey_double};
2079 Switch(elements_kind, unimplemented_elements_kind, kinds, labels,
2080 arraysize(kinds));
2081
2082 BIND(&if_fast_packed);
2083 {
2084 Comment("fast packed elements");
2085 exit_point->Return(
2086 access_mode == LoadAccessMode::kHas
2087 ? TrueConstant()
2088 : UnsafeLoadFixedArrayElement(CAST(elements), intptr_index));
2089 }
2090
2091 BIND(&if_fast_holey);
2092 {
2093 Comment("fast holey elements");
2094 TNode<Object> element =
2095 UnsafeLoadFixedArrayElement(CAST(elements), intptr_index);
2096 GotoIf(TaggedEqual(element, TheHoleConstant()), if_hole);
2097 exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant()
2098 : element);
2099 }
2100
2101 BIND(&if_fast_double);
2102 {
2103 Comment("packed double elements");
2104 if (access_mode == LoadAccessMode::kHas) {
2105 exit_point->Return(TrueConstant());
2106 } else {
2107 *var_double_value =
2108 LoadFixedDoubleArrayElement(CAST(elements), intptr_index);
2109 Goto(rebox_double);
2110 }
2111 }
2112
2113 BIND(&if_fast_holey_double);
2114 {
2115 Comment("holey double elements");
2116 TNode<Float64T> value =
2117 LoadFixedDoubleArrayElement(CAST(elements), intptr_index, if_hole);
2118 if (access_mode == LoadAccessMode::kHas) {
2119 exit_point->Return(TrueConstant());
2120 } else {
2121 *var_double_value = value;
2122 Goto(rebox_double);
2123 }
2124 }
2125 }
2126
2127 BIND(&if_nonfast);
2128 {
2129 STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
2130 GotoIf(Int32GreaterThanOrEqual(
2131 elements_kind,
2132 Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
2133 &if_typed_array);
2134 GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)),
2135 &if_dictionary);
2136 Goto(unimplemented_elements_kind);
2137
2138 BIND(&if_dictionary);
2139 {
2140 Comment("dictionary elements");
2141 if (Is64()) {
2142 GotoIf(UintPtrLessThan(IntPtrConstant(JSArray::kMaxArrayIndex),
2143 intptr_index),
2144 out_of_bounds);
2145 } else {
2146 GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
2147 }
2148
2149 TNode<FixedArrayBase> elements = LoadJSObjectElements(CAST(object));
2150 TNode<Object> value = BasicLoadNumberDictionaryElement(
2151 CAST(elements), intptr_index, miss, if_hole);
2152 exit_point->Return(access_mode == LoadAccessMode::kHas ? TrueConstant()
2153 : value);
2154 }
2155
2156 BIND(&if_typed_array);
2157 {
2158 Comment("typed elements");
2159 // Check if buffer has been detached.
2160 TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(CAST(object));
2161 GotoIf(IsDetachedBuffer(buffer), miss);
2162
2163 // Bounds check.
2164 TNode<UintPtrT> length = LoadJSTypedArrayLength(CAST(object));
2165 GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds);
2166 if (access_mode == LoadAccessMode::kHas) {
2167 exit_point->Return(TrueConstant());
2168 } else {
2169 TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(CAST(object));
2170
2171 Label uint8_elements(this), int8_elements(this), uint16_elements(this),
2172 int16_elements(this), uint32_elements(this), int32_elements(this),
2173 float32_elements(this), float64_elements(this),
2174 bigint64_elements(this), biguint64_elements(this);
2175 Label* elements_kind_labels[] = {
2176 &uint8_elements, &uint8_elements, &int8_elements,
2177 &uint16_elements, &int16_elements, &uint32_elements,
2178 &int32_elements, &float32_elements, &float64_elements,
2179 &bigint64_elements, &biguint64_elements};
2180 int32_t elements_kinds[] = {
2181 UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
2182 UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
2183 INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS,
2184 BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS};
2185 const size_t kTypedElementsKindCount =
2186 LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
2187 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1;
2188 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds));
2189 DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels));
2190 Switch(elements_kind, miss, elements_kinds, elements_kind_labels,
2191 kTypedElementsKindCount);
2192 BIND(&uint8_elements);
2193 {
2194 Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too.
2195 TNode<Int32T> element = Load<Uint8T>(data_ptr, intptr_index);
2196 exit_point->Return(SmiFromInt32(element));
2197 }
2198 BIND(&int8_elements);
2199 {
2200 Comment("INT8_ELEMENTS");
2201 TNode<Int32T> element = Load<Int8T>(data_ptr, intptr_index);
2202 exit_point->Return(SmiFromInt32(element));
2203 }
2204 BIND(&uint16_elements);
2205 {
2206 Comment("UINT16_ELEMENTS");
2207 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(1));
2208 TNode<Int32T> element = Load<Uint16T>(data_ptr, index);
2209 exit_point->Return(SmiFromInt32(element));
2210 }
2211 BIND(&int16_elements);
2212 {
2213 Comment("INT16_ELEMENTS");
2214 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(1));
2215 TNode<Int32T> element = Load<Int16T>(data_ptr, index);
2216 exit_point->Return(SmiFromInt32(element));
2217 }
2218 BIND(&uint32_elements);
2219 {
2220 Comment("UINT32_ELEMENTS");
2221 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
2222 TNode<Uint32T> element = Load<Uint32T>(data_ptr, index);
2223 exit_point->Return(ChangeUint32ToTagged(element));
2224 }
2225 BIND(&int32_elements);
2226 {
2227 Comment("INT32_ELEMENTS");
2228 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
2229 TNode<Int32T> element = Load<Int32T>(data_ptr, index);
2230 exit_point->Return(ChangeInt32ToTagged(element));
2231 }
2232 BIND(&float32_elements);
2233 {
2234 Comment("FLOAT32_ELEMENTS");
2235 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(2));
2236 TNode<Float32T> element = Load<Float32T>(data_ptr, index);
2237 *var_double_value = ChangeFloat32ToFloat64(element);
2238 Goto(rebox_double);
2239 }
2240 BIND(&float64_elements);
2241 {
2242 Comment("FLOAT64_ELEMENTS");
2243 TNode<IntPtrT> index = WordShl(intptr_index, IntPtrConstant(3));
2244 TNode<Float64T> element = Load<Float64T>(data_ptr, index);
2245 *var_double_value = element;
2246 Goto(rebox_double);
2247 }
2248 BIND(&bigint64_elements);
2249 {
2250 Comment("BIGINT64_ELEMENTS");
2251 exit_point->Return(LoadFixedTypedArrayElementAsTagged(
2252 data_ptr, Unsigned(intptr_index), BIGINT64_ELEMENTS));
2253 }
2254 BIND(&biguint64_elements);
2255 {
2256 Comment("BIGUINT64_ELEMENTS");
2257 exit_point->Return(LoadFixedTypedArrayElementAsTagged(
2258 data_ptr, Unsigned(intptr_index), BIGUINT64_ELEMENTS));
2259 }
2260 }
2261 }
2262 }
2263 }
2264
InvalidateValidityCellIfPrototype(TNode<Map> map,base::Optional<TNode<Uint32T>> maybe_bitfield3)2265 void AccessorAssembler::InvalidateValidityCellIfPrototype(
2266 TNode<Map> map, base::Optional<TNode<Uint32T>> maybe_bitfield3) {
2267 Label is_prototype(this), cont(this);
2268 TNode<Uint32T> bitfield3;
2269 if (bitfield3) {
2270 bitfield3 = maybe_bitfield3.value();
2271 } else {
2272 bitfield3 = LoadMapBitField3(map);
2273 }
2274
2275 Branch(IsSetWord32(bitfield3, Map::Bits3::IsPrototypeMapBit::kMask),
2276 &is_prototype, &cont);
2277
2278 BIND(&is_prototype);
2279 {
2280 TNode<Object> maybe_prototype_info =
2281 LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
2282 // If there's no prototype info then there's nothing to invalidate.
2283 GotoIf(TaggedIsSmi(maybe_prototype_info), &cont);
2284
2285 TNode<ExternalReference> function = ExternalConstant(
2286 ExternalReference::invalidate_prototype_chains_function());
2287 CallCFunction(function, MachineType::AnyTagged(),
2288 std::make_pair(MachineType::AnyTagged(), map));
2289 Goto(&cont);
2290 }
2291 BIND(&cont);
2292 }
2293
GenericElementLoad(TNode<HeapObject> lookup_start_object,TNode<Map> lookup_start_object_map,TNode<Int32T> lookup_start_object_instance_type,TNode<IntPtrT> index,Label * slow)2294 void AccessorAssembler::GenericElementLoad(
2295 TNode<HeapObject> lookup_start_object, TNode<Map> lookup_start_object_map,
2296 TNode<Int32T> lookup_start_object_instance_type, TNode<IntPtrT> index,
2297 Label* slow) {
2298 Comment("integer index");
2299
2300 ExitPoint direct_exit(this);
2301
2302 Label if_custom(this), if_element_hole(this), if_oob(this);
2303 Label return_undefined(this);
2304 // Receivers requiring non-standard element accesses (interceptors, access
2305 // checks, strings and string wrappers, proxies) are handled in the runtime.
2306 GotoIf(
2307 IsCustomElementsReceiverInstanceType(lookup_start_object_instance_type),
2308 &if_custom);
2309 TNode<Int32T> elements_kind = LoadMapElementsKind(lookup_start_object_map);
2310 TNode<BoolT> is_jsarray_condition =
2311 IsJSArrayInstanceType(lookup_start_object_instance_type);
2312 TVARIABLE(Float64T, var_double_value);
2313 Label rebox_double(this, &var_double_value);
2314
2315 // Unimplemented elements kinds fall back to a runtime call.
2316 Label* unimplemented_elements_kind = slow;
2317 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
2318 EmitElementLoad(lookup_start_object, elements_kind, index,
2319 is_jsarray_condition, &if_element_hole, &rebox_double,
2320 &var_double_value, unimplemented_elements_kind, &if_oob, slow,
2321 &direct_exit);
2322
2323 BIND(&rebox_double);
2324 Return(AllocateHeapNumberWithValue(var_double_value.value()));
2325
2326 BIND(&if_oob);
2327 {
2328 Comment("out of bounds");
2329 // On TypedArrays, all OOB loads (positive and negative) return undefined
2330 // without ever checking the prototype chain.
2331 GotoIf(IsJSTypedArrayInstanceType(lookup_start_object_instance_type),
2332 &return_undefined);
2333 // Positive OOB indices within JSArray index range are effectively the same
2334 // as hole loads. Larger keys and negative keys are named loads.
2335 if (Is64()) {
2336 Branch(UintPtrLessThanOrEqual(index,
2337 IntPtrConstant(JSArray::kMaxArrayIndex)),
2338 &if_element_hole, slow);
2339 } else {
2340 Branch(IntPtrLessThan(index, IntPtrConstant(0)), slow, &if_element_hole);
2341 }
2342 }
2343
2344 BIND(&if_element_hole);
2345 {
2346 Comment("found the hole");
2347 BranchIfPrototypesHaveNoElements(lookup_start_object_map, &return_undefined,
2348 slow);
2349 }
2350
2351 BIND(&if_custom);
2352 {
2353 Comment("check if string");
2354 GotoIfNot(IsStringInstanceType(lookup_start_object_instance_type), slow);
2355 Comment("load string character");
2356 TNode<IntPtrT> length = LoadStringLengthAsWord(CAST(lookup_start_object));
2357 GotoIfNot(UintPtrLessThan(index, length), slow);
2358 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1);
2359 TailCallBuiltin(Builtins::kStringCharAt, NoContextConstant(),
2360 lookup_start_object, index);
2361 }
2362
2363 BIND(&return_undefined);
2364 Return(UndefinedConstant());
2365 }
2366
GenericPropertyLoad(TNode<HeapObject> lookup_start_object,TNode<Map> lookup_start_object_map,TNode<Int32T> lookup_start_object_instance_type,const LoadICParameters * p,Label * slow,UseStubCache use_stub_cache)2367 void AccessorAssembler::GenericPropertyLoad(
2368 TNode<HeapObject> lookup_start_object, TNode<Map> lookup_start_object_map,
2369 TNode<Int32T> lookup_start_object_instance_type, const LoadICParameters* p,
2370 Label* slow, UseStubCache use_stub_cache) {
2371 DCHECK_EQ(lookup_start_object, p->lookup_start_object());
2372 ExitPoint direct_exit(this);
2373
2374 Comment("key is unique name");
2375 Label if_found_on_lookup_start_object(this), if_property_dictionary(this),
2376 lookup_prototype_chain(this), special_receiver(this);
2377 TVARIABLE(Uint32T, var_details);
2378 TVARIABLE(Object, var_value);
2379
2380 TNode<Name> name = CAST(p->name());
2381
2382 // Receivers requiring non-standard accesses (interceptors, access
2383 // checks, strings and string wrappers) are handled in the runtime.
2384 GotoIf(IsSpecialReceiverInstanceType(lookup_start_object_instance_type),
2385 &special_receiver);
2386
2387 // Check if the lookup_start_object has fast or slow properties.
2388 TNode<Uint32T> bitfield3 = LoadMapBitField3(lookup_start_object_map);
2389 GotoIf(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bitfield3),
2390 &if_property_dictionary);
2391
2392 // Try looking up the property on the lookup_start_object; if unsuccessful,
2393 // look for a handler in the stub cache.
2394 TNode<DescriptorArray> descriptors =
2395 LoadMapDescriptors(lookup_start_object_map);
2396
2397 Label if_descriptor_found(this), try_stub_cache(this);
2398 TVARIABLE(IntPtrT, var_name_index);
2399 Label* notfound = use_stub_cache == kUseStubCache ? &try_stub_cache
2400 : &lookup_prototype_chain;
2401 DescriptorLookup(name, descriptors, bitfield3, &if_descriptor_found,
2402 &var_name_index, notfound);
2403
2404 BIND(&if_descriptor_found);
2405 {
2406 LoadPropertyFromFastObject(lookup_start_object, lookup_start_object_map,
2407 descriptors, var_name_index.value(),
2408 &var_details, &var_value);
2409 Goto(&if_found_on_lookup_start_object);
2410 }
2411
2412 if (use_stub_cache == kUseStubCache) {
2413 DCHECK_EQ(lookup_start_object, p->receiver_and_lookup_start_object());
2414 Label stub_cache(this);
2415 BIND(&try_stub_cache);
2416 // When there is no feedback vector don't use stub cache.
2417 GotoIfNot(IsUndefined(p->vector()), &stub_cache);
2418 // Fall back to the slow path for private symbols.
2419 Branch(IsPrivateSymbol(name), slow, &lookup_prototype_chain);
2420
2421 BIND(&stub_cache);
2422 Comment("stub cache probe for fast property load");
2423 TVARIABLE(MaybeObject, var_handler);
2424 Label found_handler(this, &var_handler), stub_cache_miss(this);
2425 TryProbeStubCache(isolate()->load_stub_cache(), lookup_start_object, name,
2426 &found_handler, &var_handler, &stub_cache_miss);
2427 BIND(&found_handler);
2428 {
2429 LazyLoadICParameters lazy_p(p);
2430 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()),
2431 &stub_cache_miss, &direct_exit);
2432 }
2433
2434 BIND(&stub_cache_miss);
2435 {
2436 // TODO(jkummerow): Check if the property exists on the prototype
2437 // chain. If it doesn't, then there's no point in missing.
2438 Comment("KeyedLoadGeneric_miss");
2439 TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context(),
2440 p->receiver_and_lookup_start_object(), name, p->slot(),
2441 p->vector());
2442 }
2443 }
2444
2445 BIND(&if_property_dictionary);
2446 {
2447 Comment("dictionary property load");
2448 // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out
2449 // seeing global objects here (which would need special handling).
2450
2451 TVARIABLE(IntPtrT, var_name_index);
2452 Label dictionary_found(this, &var_name_index);
2453 TNode<NameDictionary> properties =
2454 CAST(LoadSlowProperties(CAST(lookup_start_object)));
2455 NameDictionaryLookup<NameDictionary>(properties, name, &dictionary_found,
2456 &var_name_index,
2457 &lookup_prototype_chain);
2458 BIND(&dictionary_found);
2459 {
2460 LoadPropertyFromNameDictionary(properties, var_name_index.value(),
2461 &var_details, &var_value);
2462 Goto(&if_found_on_lookup_start_object);
2463 }
2464 }
2465
2466 BIND(&if_found_on_lookup_start_object);
2467 {
2468 TNode<Object> value = CallGetterIfAccessor(
2469 var_value.value(), lookup_start_object, var_details.value(),
2470 p->context(), p->receiver(), slow);
2471 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1);
2472 Return(value);
2473 }
2474
2475 BIND(&lookup_prototype_chain);
2476 {
2477 TVARIABLE(Map, var_holder_map);
2478 TVARIABLE(Int32T, var_holder_instance_type);
2479 Label return_undefined(this), is_private_symbol(this);
2480 Label loop(this, {&var_holder_map, &var_holder_instance_type});
2481
2482 var_holder_map = lookup_start_object_map;
2483 var_holder_instance_type = lookup_start_object_instance_type;
2484 GotoIf(IsPrivateSymbol(name), &is_private_symbol);
2485
2486 Goto(&loop);
2487 BIND(&loop);
2488 {
2489 // Bailout if it can be an integer indexed exotic case.
2490 GotoIf(InstanceTypeEqual(var_holder_instance_type.value(),
2491 JS_TYPED_ARRAY_TYPE),
2492 slow);
2493 TNode<HeapObject> proto = LoadMapPrototype(var_holder_map.value());
2494 GotoIf(TaggedEqual(proto, NullConstant()), &return_undefined);
2495 TNode<Map> proto_map = LoadMap(proto);
2496 TNode<Uint16T> proto_instance_type = LoadMapInstanceType(proto_map);
2497 var_holder_map = proto_map;
2498 var_holder_instance_type = proto_instance_type;
2499 Label next_proto(this), return_value(this, &var_value), goto_slow(this);
2500 TryGetOwnProperty(p->context(), p->receiver(), CAST(proto), proto_map,
2501 proto_instance_type, name, &return_value, &var_value,
2502 &next_proto, &goto_slow);
2503
2504 // This trampoline and the next are required to appease Turbofan's
2505 // variable merging.
2506 BIND(&next_proto);
2507 Goto(&loop);
2508
2509 BIND(&goto_slow);
2510 Goto(slow);
2511
2512 BIND(&return_value);
2513 Return(var_value.value());
2514 }
2515
2516 BIND(&is_private_symbol);
2517 {
2518 CSA_ASSERT(this, IsPrivateSymbol(name));
2519
2520 // For private names that don't exist on the receiver, we bail
2521 // to the runtime to throw. For private symbols, we just return
2522 // undefined.
2523 Branch(IsPrivateName(CAST(name)), slow, &return_undefined);
2524 }
2525
2526 BIND(&return_undefined);
2527 Return(UndefinedConstant());
2528 }
2529
2530 BIND(&special_receiver);
2531 {
2532 // TODO(jkummerow): Consider supporting JSModuleNamespace.
2533 GotoIfNot(
2534 InstanceTypeEqual(lookup_start_object_instance_type, JS_PROXY_TYPE),
2535 slow);
2536
2537 // Private field/symbol lookup is not supported.
2538 GotoIf(IsPrivateSymbol(name), slow);
2539
2540 direct_exit.ReturnCallStub(
2541 Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
2542 p->context(), lookup_start_object, name, p->receiver(),
2543 SmiConstant(OnNonExistent::kReturnUndefined));
2544 }
2545 }
2546
2547 //////////////////// Stub cache access helpers.
2548
2549 enum AccessorAssembler::StubCacheTable : int {
2550 kPrimary = static_cast<int>(StubCache::kPrimary),
2551 kSecondary = static_cast<int>(StubCache::kSecondary)
2552 };
2553
StubCachePrimaryOffset(TNode<Name> name,TNode<Map> map)2554 TNode<IntPtrT> AccessorAssembler::StubCachePrimaryOffset(TNode<Name> name,
2555 TNode<Map> map) {
2556 // Compute the hash of the name (use entire hash field).
2557 TNode<Uint32T> hash_field = LoadNameHashField(name);
2558 CSA_ASSERT(this,
2559 Word32Equal(Word32And(hash_field,
2560 Int32Constant(Name::kHashNotComputedMask)),
2561 Int32Constant(0)));
2562
2563 // Using only the low bits in 64-bit mode is unlikely to increase the
2564 // risk of collision even if the heap is spread over an area larger than
2565 // 4Gb (and not at all if it isn't).
2566 TNode<IntPtrT> map_word = BitcastTaggedToWord(map);
2567
2568 TNode<Int32T> map32 = TruncateIntPtrToInt32(UncheckedCast<IntPtrT>(
2569 WordXor(map_word, WordShr(map_word, StubCache::kMapKeyShift))));
2570 // Base the offset on a simple combination of name and map.
2571 TNode<Word32T> hash = Int32Add(hash_field, map32);
2572 uint32_t mask = (StubCache::kPrimaryTableSize - 1)
2573 << StubCache::kCacheIndexShift;
2574 TNode<UintPtrT> result =
2575 ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
2576 return Signed(result);
2577 }
2578
StubCacheSecondaryOffset(TNode<Name> name,TNode<IntPtrT> seed)2579 TNode<IntPtrT> AccessorAssembler::StubCacheSecondaryOffset(
2580 TNode<Name> name, TNode<IntPtrT> seed) {
2581 // See v8::internal::StubCache::SecondaryOffset().
2582
2583 // Use the seed from the primary cache in the secondary cache.
2584 TNode<Int32T> name32 = TruncateIntPtrToInt32(BitcastTaggedToWord(name));
2585 TNode<Int32T> hash = Int32Sub(TruncateIntPtrToInt32(seed), name32);
2586 hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
2587 int32_t mask = (StubCache::kSecondaryTableSize - 1)
2588 << StubCache::kCacheIndexShift;
2589 TNode<UintPtrT> result =
2590 ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
2591 return Signed(result);
2592 }
2593
TryProbeStubCacheTable(StubCache * stub_cache,StubCacheTable table_id,TNode<IntPtrT> entry_offset,TNode<Object> name,TNode<Map> map,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)2594 void AccessorAssembler::TryProbeStubCacheTable(
2595 StubCache* stub_cache, StubCacheTable table_id, TNode<IntPtrT> entry_offset,
2596 TNode<Object> name, TNode<Map> map, Label* if_handler,
2597 TVariable<MaybeObject>* var_handler, Label* if_miss) {
2598 StubCache::Table table = static_cast<StubCache::Table>(table_id);
2599 // The {table_offset} holds the entry offset times four (due to masking
2600 // and shifting optimizations).
2601 const int kMultiplier =
2602 sizeof(StubCache::Entry) >> StubCache::kCacheIndexShift;
2603 entry_offset = IntPtrMul(entry_offset, IntPtrConstant(kMultiplier));
2604
2605 TNode<ExternalReference> key_base = ExternalConstant(
2606 ExternalReference::Create(stub_cache->key_reference(table)));
2607
2608 // Check that the key in the entry matches the name.
2609 DCHECK_EQ(0, offsetof(StubCache::Entry, key));
2610 TNode<HeapObject> cached_key =
2611 CAST(Load(MachineType::TaggedPointer(), key_base, entry_offset));
2612 GotoIf(TaggedNotEqual(name, cached_key), if_miss);
2613
2614 // Check that the map in the entry matches.
2615 TNode<Object> cached_map = Load<Object>(
2616 key_base,
2617 IntPtrAdd(entry_offset, IntPtrConstant(offsetof(StubCache::Entry, map))));
2618 GotoIf(TaggedNotEqual(map, cached_map), if_miss);
2619
2620 TNode<MaybeObject> handler = ReinterpretCast<MaybeObject>(
2621 Load(MachineType::AnyTagged(), key_base,
2622 IntPtrAdd(entry_offset,
2623 IntPtrConstant(offsetof(StubCache::Entry, value)))));
2624
2625 // We found the handler.
2626 *var_handler = handler;
2627 Goto(if_handler);
2628 }
2629
TryProbeStubCache(StubCache * stub_cache,TNode<Object> lookup_start_object,TNode<Name> name,Label * if_handler,TVariable<MaybeObject> * var_handler,Label * if_miss)2630 void AccessorAssembler::TryProbeStubCache(StubCache* stub_cache,
2631 TNode<Object> lookup_start_object,
2632 TNode<Name> name, Label* if_handler,
2633 TVariable<MaybeObject>* var_handler,
2634 Label* if_miss) {
2635 Label try_secondary(this), miss(this);
2636
2637 Counters* counters = isolate()->counters();
2638 IncrementCounter(counters->megamorphic_stub_cache_probes(), 1);
2639
2640 // Check that the {lookup_start_object} isn't a smi.
2641 GotoIf(TaggedIsSmi(lookup_start_object), &miss);
2642
2643 TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
2644
2645 // Probe the primary table.
2646 TNode<IntPtrT> primary_offset =
2647 StubCachePrimaryOffset(name, lookup_start_object_map);
2648 TryProbeStubCacheTable(stub_cache, kPrimary, primary_offset, name,
2649 lookup_start_object_map, if_handler, var_handler,
2650 &try_secondary);
2651
2652 BIND(&try_secondary);
2653 {
2654 // Probe the secondary table.
2655 TNode<IntPtrT> secondary_offset =
2656 StubCacheSecondaryOffset(name, primary_offset);
2657 TryProbeStubCacheTable(stub_cache, kSecondary, secondary_offset, name,
2658 lookup_start_object_map, if_handler, var_handler,
2659 &miss);
2660 }
2661
2662 BIND(&miss);
2663 {
2664 IncrementCounter(counters->megamorphic_stub_cache_misses(), 1);
2665 Goto(if_miss);
2666 }
2667 }
2668
2669 //////////////////// Entry points into private implementation (one per stub).
2670
LoadIC_BytecodeHandler(const LazyLoadICParameters * p,ExitPoint * exit_point)2671 void AccessorAssembler::LoadIC_BytecodeHandler(const LazyLoadICParameters* p,
2672 ExitPoint* exit_point) {
2673 // Must be kept in sync with LoadIC.
2674
2675 // This function is hand-tuned to omit frame construction for common cases,
2676 // e.g.: monomorphic field and constant loads through smi handlers.
2677 // Polymorphic ICs with a hit in the first two entries also omit frames.
2678 // TODO(jgruber): Frame omission is fragile and can be affected by minor
2679 // changes in control flow and logic. We currently have no way of ensuring
2680 // that no frame is constructed, so it's easy to break this optimization by
2681 // accident.
2682 Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred),
2683 no_feedback(this, Label::kDeferred);
2684
2685 GotoIf(IsUndefined(p->vector()), &no_feedback);
2686
2687 TNode<Map> lookup_start_object_map =
2688 LoadReceiverMap(p->receiver_and_lookup_start_object());
2689 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
2690
2691 // Inlined fast path.
2692 {
2693 Comment("LoadIC_BytecodeHandler_fast");
2694
2695 TVARIABLE(MaybeObject, var_handler);
2696 Label try_polymorphic(this), if_handler(this, &var_handler);
2697
2698 TNode<MaybeObject> feedback = TryMonomorphicCase(
2699 p->slot(), CAST(p->vector()), lookup_start_object_map, &if_handler,
2700 &var_handler, &try_polymorphic);
2701
2702 BIND(&if_handler);
2703 HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, exit_point);
2704
2705 BIND(&try_polymorphic);
2706 {
2707 TNode<HeapObject> strong_feedback =
2708 GetHeapObjectIfStrong(feedback, &miss);
2709 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &stub_call);
2710 HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
2711 &if_handler, &var_handler, &miss);
2712 }
2713 }
2714
2715 BIND(&stub_call);
2716 {
2717 Comment("LoadIC_BytecodeHandler_noninlined");
2718
2719 // Call into the stub that implements the non-inlined parts of LoadIC.
2720 Callable ic =
2721 Builtins::CallableFor(isolate(), Builtins::kLoadIC_Noninlined);
2722 TNode<Code> code_target = HeapConstant(ic.code());
2723 exit_point->ReturnCallStub(ic.descriptor(), code_target, p->context(),
2724 p->receiver_and_lookup_start_object(), p->name(),
2725 p->slot(), p->vector());
2726 }
2727
2728 BIND(&no_feedback);
2729 {
2730 Comment("LoadIC_BytecodeHandler_nofeedback");
2731 // Call into the stub that implements the non-inlined parts of LoadIC.
2732 exit_point->ReturnCallStub(
2733 Builtins::CallableFor(isolate(), Builtins::kLoadIC_NoFeedback),
2734 p->context(), p->receiver(), p->name(),
2735 SmiConstant(FeedbackSlotKind::kLoadProperty));
2736 }
2737
2738 BIND(&miss);
2739 {
2740 Comment("LoadIC_BytecodeHandler_miss");
2741
2742 exit_point->ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context(),
2743 p->receiver(), p->name(), p->slot(),
2744 p->vector());
2745 }
2746 }
2747
LoadIC(const LoadICParameters * p)2748 void AccessorAssembler::LoadIC(const LoadICParameters* p) {
2749 // Must be kept in sync with LoadIC_BytecodeHandler.
2750
2751 ExitPoint direct_exit(this);
2752
2753 TVARIABLE(MaybeObject, var_handler);
2754 Label if_handler(this, &var_handler), non_inlined(this, Label::kDeferred),
2755 try_polymorphic(this), miss(this, Label::kDeferred);
2756
2757 TNode<Map> lookup_start_object_map =
2758 LoadReceiverMap(p->receiver_and_lookup_start_object());
2759 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
2760
2761 // Check monomorphic case.
2762 TNode<MaybeObject> feedback =
2763 TryMonomorphicCase(p->slot(), CAST(p->vector()), lookup_start_object_map,
2764 &if_handler, &var_handler, &try_polymorphic);
2765 BIND(&if_handler);
2766 {
2767 LazyLoadICParameters lazy_p(p);
2768 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
2769 &direct_exit);
2770 }
2771
2772 BIND(&try_polymorphic);
2773 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
2774 {
2775 // Check polymorphic case.
2776 Comment("LoadIC_try_polymorphic");
2777 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined);
2778 HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
2779 &if_handler, &var_handler, &miss);
2780 }
2781
2782 BIND(&non_inlined);
2783 {
2784 LoadIC_Noninlined(p, lookup_start_object_map, strong_feedback, &var_handler,
2785 &if_handler, &miss, &direct_exit);
2786 }
2787
2788 BIND(&miss);
2789 direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context(),
2790 p->receiver_and_lookup_start_object(),
2791 p->name(), p->slot(), p->vector());
2792 }
2793
LoadSuperIC(const LoadICParameters * p)2794 void AccessorAssembler::LoadSuperIC(const LoadICParameters* p) {
2795 ExitPoint direct_exit(this);
2796
2797 TVARIABLE(MaybeObject, var_handler);
2798 Label if_handler(this, &var_handler), no_feedback(this),
2799 non_inlined(this, Label::kDeferred), try_polymorphic(this),
2800 miss(this, Label::kDeferred);
2801
2802 GotoIf(IsUndefined(p->vector()), &no_feedback);
2803
2804 // The lookup start object cannot be a SMI, since it's the home object's
2805 // prototype, and it's not possible to set SMIs as prototypes.
2806 TNode<Map> lookup_start_object_map =
2807 LoadReceiverMap(p->lookup_start_object());
2808 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
2809
2810 TNode<MaybeObject> feedback =
2811 TryMonomorphicCase(p->slot(), CAST(p->vector()), lookup_start_object_map,
2812 &if_handler, &var_handler, &try_polymorphic);
2813
2814 BIND(&if_handler);
2815 {
2816 LazyLoadICParameters lazy_p(p);
2817 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
2818 &direct_exit);
2819 }
2820
2821 BIND(&no_feedback);
2822 { LoadSuperIC_NoFeedback(p); }
2823
2824 BIND(&try_polymorphic);
2825 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
2826 {
2827 Comment("LoadSuperIC_try_polymorphic");
2828 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined);
2829 HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
2830 &if_handler, &var_handler, &miss);
2831 }
2832
2833 BIND(&non_inlined);
2834 {
2835 // LoadIC_Noninlined can be used here, since it handles the
2836 // lookup_start_object != receiver case gracefully.
2837 LoadIC_Noninlined(p, lookup_start_object_map, strong_feedback, &var_handler,
2838 &if_handler, &miss, &direct_exit);
2839 }
2840
2841 BIND(&miss);
2842 direct_exit.ReturnCallRuntime(Runtime::kLoadWithReceiverIC_Miss, p->context(),
2843 p->receiver(), p->lookup_start_object(),
2844 p->name(), p->slot(), p->vector());
2845 }
2846
LoadIC_Noninlined(const LoadICParameters * p,TNode<Map> lookup_start_object_map,TNode<HeapObject> feedback,TVariable<MaybeObject> * var_handler,Label * if_handler,Label * miss,ExitPoint * exit_point)2847 void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p,
2848 TNode<Map> lookup_start_object_map,
2849 TNode<HeapObject> feedback,
2850 TVariable<MaybeObject>* var_handler,
2851 Label* if_handler, Label* miss,
2852 ExitPoint* exit_point) {
2853 // Neither deprecated map nor monomorphic. These cases are handled in the
2854 // bytecode handler.
2855 CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(lookup_start_object_map)));
2856 CSA_ASSERT(this, TaggedNotEqual(lookup_start_object_map, feedback));
2857 CSA_ASSERT(this, Word32BinaryNot(IsWeakFixedArrayMap(LoadMap(feedback))));
2858 DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
2859
2860 {
2861 // Check megamorphic case.
2862 GotoIfNot(TaggedEqual(feedback, MegamorphicSymbolConstant()), miss);
2863
2864 TryProbeStubCache(isolate()->load_stub_cache(), p->lookup_start_object(),
2865 CAST(p->name()), if_handler, var_handler, miss);
2866 }
2867 }
2868
LoadIC_NoFeedback(const LoadICParameters * p,TNode<Smi> ic_kind)2869 void AccessorAssembler::LoadIC_NoFeedback(const LoadICParameters* p,
2870 TNode<Smi> ic_kind) {
2871 Label miss(this, Label::kDeferred);
2872 TNode<Object> lookup_start_object = p->receiver_and_lookup_start_object();
2873 GotoIf(TaggedIsSmi(lookup_start_object), &miss);
2874 TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
2875 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
2876
2877 TNode<Uint16T> instance_type = LoadMapInstanceType(lookup_start_object_map);
2878
2879 {
2880 // Special case for Function.prototype load, because it's very common
2881 // for ICs that are only executed once (MyFunc.prototype.foo = ...).
2882 Label not_function_prototype(this, Label::kDeferred);
2883 GotoIfNot(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE),
2884 ¬_function_prototype);
2885 GotoIfNot(IsPrototypeString(p->name()), ¬_function_prototype);
2886
2887 GotoIfPrototypeRequiresRuntimeLookup(CAST(lookup_start_object),
2888 lookup_start_object_map,
2889 ¬_function_prototype);
2890 Return(LoadJSFunctionPrototype(CAST(lookup_start_object), &miss));
2891 BIND(¬_function_prototype);
2892 }
2893
2894 GenericPropertyLoad(CAST(lookup_start_object), lookup_start_object_map,
2895 instance_type, p, &miss, kDontUseStubCache);
2896
2897 BIND(&miss);
2898 {
2899 TailCallRuntime(Runtime::kLoadNoFeedbackIC_Miss, p->context(),
2900 p->receiver(), p->name(), ic_kind);
2901 }
2902 }
2903
LoadSuperIC_NoFeedback(const LoadICParameters * p)2904 void AccessorAssembler::LoadSuperIC_NoFeedback(const LoadICParameters* p) {
2905 Label miss(this, Label::kDeferred);
2906 TNode<Object> lookup_start_object = p->lookup_start_object();
2907
2908 // The lookup start object cannot be a SMI, since it's the home object's
2909 // prototype, and it's not possible to set SMIs as prototypes.
2910 TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
2911 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
2912
2913 TNode<Uint16T> instance_type = LoadMapInstanceType(lookup_start_object_map);
2914
2915 GenericPropertyLoad(CAST(lookup_start_object), lookup_start_object_map,
2916 instance_type, p, &miss, kDontUseStubCache);
2917
2918 BIND(&miss);
2919 {
2920 TailCallRuntime(Runtime::kLoadWithReceiverNoFeedbackIC_Miss, p->context(),
2921 p->receiver(), p->lookup_start_object(), p->name());
2922 }
2923 }
2924
LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector,const LazyNode<TaggedIndex> & lazy_slot,const LazyNode<Context> & lazy_context,const LazyNode<Name> & lazy_name,TypeofMode typeof_mode,ExitPoint * exit_point)2925 void AccessorAssembler::LoadGlobalIC(TNode<HeapObject> maybe_feedback_vector,
2926 const LazyNode<TaggedIndex>& lazy_slot,
2927 const LazyNode<Context>& lazy_context,
2928 const LazyNode<Name>& lazy_name,
2929 TypeofMode typeof_mode,
2930 ExitPoint* exit_point) {
2931 Label try_handler(this, Label::kDeferred), miss(this, Label::kDeferred),
2932 no_feedback(this, Label::kDeferred);
2933
2934 GotoIf(IsUndefined(maybe_feedback_vector), &no_feedback);
2935 {
2936 TNode<TaggedIndex> slot = lazy_slot();
2937
2938 {
2939 TNode<FeedbackVector> vector = CAST(maybe_feedback_vector);
2940 LoadGlobalIC_TryPropertyCellCase(vector, slot, lazy_context, exit_point,
2941 &try_handler, &miss);
2942
2943 BIND(&try_handler);
2944 LoadGlobalIC_TryHandlerCase(vector, slot, lazy_context, lazy_name,
2945 typeof_mode, exit_point, &miss);
2946 }
2947
2948 BIND(&miss);
2949 {
2950 Comment("LoadGlobalIC_MissCase");
2951 TNode<Context> context = lazy_context();
2952 TNode<Name> name = lazy_name();
2953 exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Miss, context, name,
2954 slot, maybe_feedback_vector,
2955 SmiConstant(typeof_mode));
2956 }
2957 }
2958
2959 BIND(&no_feedback);
2960 {
2961 int ic_kind =
2962 static_cast<int>((typeof_mode == INSIDE_TYPEOF)
2963 ? FeedbackSlotKind::kLoadGlobalInsideTypeof
2964 : FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
2965 exit_point->ReturnCallStub(
2966 Builtins::CallableFor(isolate(), Builtins::kLoadGlobalIC_NoFeedback),
2967 lazy_context(), lazy_name(), SmiConstant(ic_kind));
2968 }
2969 }
2970
LoadGlobalIC_TryPropertyCellCase(TNode<FeedbackVector> vector,TNode<TaggedIndex> slot,const LazyNode<Context> & lazy_context,ExitPoint * exit_point,Label * try_handler,Label * miss)2971 void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase(
2972 TNode<FeedbackVector> vector, TNode<TaggedIndex> slot,
2973 const LazyNode<Context>& lazy_context, ExitPoint* exit_point,
2974 Label* try_handler, Label* miss) {
2975 Comment("LoadGlobalIC_TryPropertyCellCase");
2976
2977 Label if_lexical_var(this), if_property_cell(this);
2978 TNode<MaybeObject> maybe_weak_ref = LoadFeedbackVectorSlot(vector, slot);
2979 Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell);
2980
2981 BIND(&if_property_cell);
2982 {
2983 // Load value or try handler case if the weak reference is cleared.
2984 CSA_ASSERT(this, IsWeakOrCleared(maybe_weak_ref));
2985 TNode<PropertyCell> property_cell =
2986 CAST(GetHeapObjectAssumeWeak(maybe_weak_ref, try_handler));
2987 TNode<Object> value =
2988 LoadObjectField(property_cell, PropertyCell::kValueOffset);
2989 GotoIf(TaggedEqual(value, TheHoleConstant()), miss);
2990 exit_point->Return(value);
2991 }
2992
2993 BIND(&if_lexical_var);
2994 {
2995 Comment("Load lexical variable");
2996 TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref));
2997 TNode<IntPtrT> context_index =
2998 Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler));
2999 TNode<IntPtrT> slot_index =
3000 Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler));
3001 TNode<Context> context = lazy_context();
3002 TNode<Context> script_context = LoadScriptContext(context, context_index);
3003 TNode<Object> result = LoadContextElement(script_context, slot_index);
3004 exit_point->Return(result);
3005 }
3006 }
3007
LoadGlobalIC_TryHandlerCase(TNode<FeedbackVector> vector,TNode<TaggedIndex> slot,const LazyNode<Context> & lazy_context,const LazyNode<Name> & lazy_name,TypeofMode typeof_mode,ExitPoint * exit_point,Label * miss)3008 void AccessorAssembler::LoadGlobalIC_TryHandlerCase(
3009 TNode<FeedbackVector> vector, TNode<TaggedIndex> slot,
3010 const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name,
3011 TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss) {
3012 Comment("LoadGlobalIC_TryHandlerCase");
3013
3014 Label call_handler(this), non_smi(this);
3015
3016 TNode<MaybeObject> feedback_element =
3017 LoadFeedbackVectorSlot(vector, slot, kTaggedSize);
3018 TNode<Object> handler = CAST(feedback_element);
3019 GotoIf(TaggedEqual(handler, UninitializedSymbolConstant()), miss);
3020
3021 OnNonExistent on_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF
3022 ? OnNonExistent::kThrowReferenceError
3023 : OnNonExistent::kReturnUndefined;
3024
3025 TNode<Context> context = lazy_context();
3026 TNode<NativeContext> native_context = LoadNativeContext(context);
3027 TNode<JSGlobalProxy> receiver =
3028 CAST(LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX));
3029 TNode<Object> global =
3030 LoadContextElement(native_context, Context::EXTENSION_INDEX);
3031
3032 LazyLoadICParameters p([=] { return context; }, receiver, lazy_name,
3033 [=] { return slot; }, vector, global);
3034
3035 HandleLoadICHandlerCase(&p, handler, miss, exit_point, ICMode::kGlobalIC,
3036 on_nonexistent);
3037 }
3038
ScriptContextTableLookup(TNode<Name> name,TNode<NativeContext> native_context,Label * found_hole,Label * not_found)3039 void AccessorAssembler::ScriptContextTableLookup(
3040 TNode<Name> name, TNode<NativeContext> native_context, Label* found_hole,
3041 Label* not_found) {
3042 TNode<ScriptContextTable> script_context_table = CAST(
3043 LoadContextElement(native_context, Context::SCRIPT_CONTEXT_TABLE_INDEX));
3044 TVARIABLE(IntPtrT, context_index, IntPtrConstant(-1));
3045 Label loop(this, &context_index);
3046 TNode<IntPtrT> num_script_contexts = SmiUntag(CAST(LoadFixedArrayElement(
3047 script_context_table, ScriptContextTable::kUsedSlotIndex)));
3048 Goto(&loop);
3049
3050 BIND(&loop);
3051 {
3052 context_index = IntPtrAdd(context_index.value(), IntPtrConstant(1));
3053 GotoIf(IntPtrGreaterThanOrEqual(context_index.value(), num_script_contexts),
3054 not_found);
3055
3056 TNode<Context> script_context = CAST(LoadFixedArrayElement(
3057 script_context_table, context_index.value(),
3058 ScriptContextTable::kFirstContextSlotIndex * kTaggedSize));
3059 TNode<ScopeInfo> scope_info =
3060 CAST(LoadContextElement(script_context, Context::SCOPE_INFO_INDEX));
3061 TNode<IntPtrT> length = LoadAndUntagFixedArrayBaseLength(scope_info);
3062 GotoIf(IntPtrLessThanOrEqual(length, IntPtrConstant(0)), &loop);
3063
3064 TVARIABLE(IntPtrT, scope_var_index,
3065 IntPtrConstant(ScopeInfo::kVariablePartIndex - 1));
3066 TNode<IntPtrT> num_scope_vars = SmiUntag(CAST(LoadFixedArrayElement(
3067 scope_info, IntPtrConstant(ScopeInfo::Fields::kContextLocalCount))));
3068 TNode<IntPtrT> end_index = IntPtrAdd(
3069 num_scope_vars, IntPtrConstant(ScopeInfo::kVariablePartIndex));
3070 Label loop_scope_info(this, &scope_var_index);
3071 Goto(&loop_scope_info);
3072
3073 BIND(&loop_scope_info);
3074 {
3075 scope_var_index = IntPtrAdd(scope_var_index.value(), IntPtrConstant(1));
3076 GotoIf(IntPtrGreaterThanOrEqual(scope_var_index.value(), end_index),
3077 &loop);
3078
3079 TNode<Object> var_name =
3080 LoadFixedArrayElement(scope_info, scope_var_index.value(), 0);
3081 GotoIf(TaggedNotEqual(var_name, name), &loop_scope_info);
3082
3083 TNode<IntPtrT> var_index =
3084 IntPtrAdd(IntPtrConstant(Context::MIN_CONTEXT_SLOTS),
3085 IntPtrSub(scope_var_index.value(),
3086 IntPtrConstant(ScopeInfo::kVariablePartIndex)));
3087 TNode<Object> result = LoadContextElement(script_context, var_index);
3088 GotoIf(IsTheHole(result), found_hole);
3089 Return(result);
3090 }
3091 }
3092 }
3093
LoadGlobalIC_NoFeedback(TNode<Context> context,TNode<Object> name,TNode<Smi> smi_typeof_mode)3094 void AccessorAssembler::LoadGlobalIC_NoFeedback(TNode<Context> context,
3095 TNode<Object> name,
3096 TNode<Smi> smi_typeof_mode) {
3097 TNode<NativeContext> native_context = LoadNativeContext(context);
3098 Label regular_load(this), throw_reference_error(this, Label::kDeferred);
3099
3100 GotoIfNot(IsString(CAST(name)), ®ular_load);
3101 ScriptContextTableLookup(CAST(name), native_context, &throw_reference_error,
3102 ®ular_load);
3103
3104 BIND(&throw_reference_error);
3105 Return(CallRuntime(Runtime::kThrowReferenceError, context, name));
3106
3107 BIND(®ular_load);
3108 TNode<JSGlobalObject> global_object =
3109 CAST(LoadContextElement(native_context, Context::EXTENSION_INDEX));
3110 TailCallStub(Builtins::CallableFor(isolate(), Builtins::kLoadIC_NoFeedback),
3111 context, global_object, name, smi_typeof_mode);
3112 }
3113
KeyedLoadIC(const LoadICParameters * p,LoadAccessMode access_mode)3114 void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p,
3115 LoadAccessMode access_mode) {
3116 ExitPoint direct_exit(this);
3117
3118 TVARIABLE(MaybeObject, var_handler);
3119 Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred),
3120 try_megamorphic(this, Label::kDeferred),
3121 try_uninitialized(this, Label::kDeferred),
3122 try_polymorphic_name(this, Label::kDeferred),
3123 miss(this, Label::kDeferred), generic(this, Label::kDeferred);
3124
3125 TNode<Map> lookup_start_object_map =
3126 LoadReceiverMap(p->receiver_and_lookup_start_object());
3127 GotoIf(IsDeprecatedMap(lookup_start_object_map), &miss);
3128
3129 GotoIf(IsUndefined(p->vector()), &generic);
3130
3131 // Check monomorphic case.
3132 TNode<MaybeObject> feedback =
3133 TryMonomorphicCase(p->slot(), CAST(p->vector()), lookup_start_object_map,
3134 &if_handler, &var_handler, &try_polymorphic);
3135 BIND(&if_handler);
3136 {
3137 LazyLoadICParameters lazy_p(p);
3138 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
3139 &direct_exit, ICMode::kNonGlobalIC,
3140 OnNonExistent::kReturnUndefined, kSupportElements,
3141 access_mode);
3142 }
3143
3144 BIND(&try_polymorphic);
3145 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
3146 {
3147 // Check polymorphic case.
3148 Comment("KeyedLoadIC_try_polymorphic");
3149 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
3150 HandlePolymorphicCase(lookup_start_object_map, CAST(strong_feedback),
3151 &if_handler, &var_handler, &miss);
3152 }
3153
3154 BIND(&try_megamorphic);
3155 {
3156 // Check megamorphic case.
3157 Comment("KeyedLoadIC_try_megamorphic");
3158 Branch(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()), &generic,
3159 &try_uninitialized);
3160 }
3161
3162 BIND(&generic);
3163 {
3164 // TODO(jkummerow): Inline this? Or some of it?
3165 TailCallBuiltin(access_mode == LoadAccessMode::kLoad
3166 ? Builtins::kKeyedLoadIC_Megamorphic
3167 : Builtins::kKeyedHasIC_Megamorphic,
3168 p->context(), p->receiver(), p->name(), p->slot(),
3169 p->vector());
3170 }
3171
3172 BIND(&try_uninitialized);
3173 {
3174 // Check uninitialized case.
3175 Comment("KeyedLoadIC_try_uninitialized");
3176 Branch(TaggedEqual(strong_feedback, UninitializedSymbolConstant()), &miss,
3177 &try_polymorphic_name);
3178 }
3179
3180 BIND(&try_polymorphic_name);
3181 {
3182 // We might have a name in feedback, and a weak fixed array in the next
3183 // slot.
3184 Comment("KeyedLoadIC_try_polymorphic_name");
3185 TVARIABLE(Name, var_name);
3186 TVARIABLE(IntPtrT, var_index);
3187 Label if_polymorphic_name(this), feedback_matches(this),
3188 if_internalized(this), if_notinternalized(this, Label::kDeferred);
3189
3190 // Fast-case: The recorded {feedback} matches the {name}.
3191 GotoIf(TaggedEqual(strong_feedback, p->name()), &feedback_matches);
3192
3193 // Try to internalize the {name} if it isn't already.
3194 TryToName(p->name(), &miss, &var_index, &if_internalized, &var_name, &miss,
3195 &if_notinternalized);
3196
3197 BIND(&if_internalized);
3198 {
3199 // The {var_name} now contains a unique name.
3200 Branch(TaggedEqual(strong_feedback, var_name.value()),
3201 &if_polymorphic_name, &miss);
3202 }
3203
3204 BIND(&if_notinternalized);
3205 {
3206 TVARIABLE(IntPtrT, var_index);
3207 TryInternalizeString(CAST(p->name()), &miss, &var_index, &if_internalized,
3208 &var_name, &miss, &miss);
3209 }
3210
3211 BIND(&feedback_matches);
3212 {
3213 var_name = CAST(p->name());
3214 Goto(&if_polymorphic_name);
3215 }
3216
3217 BIND(&if_polymorphic_name);
3218 {
3219 // If the name comparison succeeded, we know we have a weak fixed array
3220 // with at least one map/handler pair.
3221 TailCallBuiltin(access_mode == LoadAccessMode::kLoad
3222 ? Builtins::kKeyedLoadIC_PolymorphicName
3223 : Builtins::kKeyedHasIC_PolymorphicName,
3224 p->context(), p->receiver(), var_name.value(), p->slot(),
3225 p->vector());
3226 }
3227 }
3228
3229 BIND(&miss);
3230 {
3231 Comment("KeyedLoadIC_miss");
3232 TailCallRuntime(
3233 access_mode == LoadAccessMode::kLoad ? Runtime::kKeyedLoadIC_Miss
3234 : Runtime::kKeyedHasIC_Miss,
3235 p->context(), p->receiver(), p->name(), p->slot(), p->vector());
3236 }
3237 }
3238
KeyedLoadICGeneric(const LoadICParameters * p)3239 void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
3240 TVARIABLE(Object, var_name, p->name());
3241
3242 Label if_runtime(this, Label::kDeferred);
3243 TNode<Object> lookup_start_object = p->lookup_start_object();
3244 GotoIf(TaggedIsSmi(lookup_start_object), &if_runtime);
3245 GotoIf(IsNullOrUndefined(lookup_start_object), &if_runtime);
3246
3247 {
3248 TVARIABLE(IntPtrT, var_index);
3249 TVARIABLE(Name, var_unique);
3250 Label if_index(this), if_unique_name(this, &var_name), if_notunique(this),
3251 if_other(this, Label::kDeferred);
3252
3253 TryToName(var_name.value(), &if_index, &var_index, &if_unique_name,
3254 &var_unique, &if_other, &if_notunique);
3255
3256 BIND(&if_unique_name);
3257 {
3258 LoadICParameters pp(p, var_unique.value());
3259 TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
3260 GenericPropertyLoad(CAST(lookup_start_object), lookup_start_object_map,
3261 LoadMapInstanceType(lookup_start_object_map), &pp,
3262 &if_runtime);
3263 }
3264
3265 BIND(&if_other);
3266 {
3267 var_name = CallBuiltin(Builtins::kToName, p->context(), var_name.value());
3268 TryToName(var_name.value(), &if_index, &var_index, &if_unique_name,
3269 &var_unique, &if_runtime, &if_notunique);
3270 }
3271
3272 BIND(&if_notunique);
3273 {
3274 if (FLAG_internalize_on_the_fly) {
3275 // Ideally we could return undefined directly here if the name is not
3276 // found in the string table, i.e. it was never internalized, but that
3277 // invariant doesn't hold with named property interceptors (at this
3278 // point), so we take the {if_runtime} path instead.
3279 Label if_in_string_table(this);
3280 TryInternalizeString(CAST(var_name.value()), &if_index, &var_index,
3281 &if_in_string_table, &var_unique, &if_runtime,
3282 &if_runtime);
3283
3284 BIND(&if_in_string_table);
3285 {
3286 // TODO(bmeurer): We currently use a version of GenericPropertyLoad
3287 // here, where we don't try to probe the megamorphic stub cache
3288 // after successfully internalizing the incoming string. Past
3289 // experiments with this have shown that it causes too much traffic
3290 // on the stub cache. We may want to re-evaluate that in the future.
3291 LoadICParameters pp(p, var_unique.value());
3292 TNode<Map> lookup_start_object_map =
3293 LoadMap(CAST(lookup_start_object));
3294 GenericPropertyLoad(CAST(lookup_start_object),
3295 lookup_start_object_map,
3296 LoadMapInstanceType(lookup_start_object_map), &pp,
3297 &if_runtime, kDontUseStubCache);
3298 }
3299 } else {
3300 Goto(&if_runtime);
3301 }
3302 }
3303
3304 BIND(&if_index);
3305 {
3306 TNode<Map> lookup_start_object_map = LoadMap(CAST(lookup_start_object));
3307 GenericElementLoad(CAST(lookup_start_object), lookup_start_object_map,
3308 LoadMapInstanceType(lookup_start_object_map),
3309 var_index.value(), &if_runtime);
3310 }
3311 }
3312
3313 BIND(&if_runtime);
3314 {
3315 Comment("KeyedLoadGeneric_slow");
3316 IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1);
3317 // TODO(jkummerow): Should we use the GetProperty TF stub instead?
3318 TailCallRuntime(Runtime::kGetProperty, p->context(),
3319 p->receiver_and_lookup_start_object(), var_name.value());
3320 }
3321 }
3322
KeyedLoadICPolymorphicName(const LoadICParameters * p,LoadAccessMode access_mode)3323 void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p,
3324 LoadAccessMode access_mode) {
3325 TVARIABLE(MaybeObject, var_handler);
3326 Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
3327
3328 TNode<Object> lookup_start_object = p->lookup_start_object();
3329 TNode<Map> lookup_start_object_map = LoadReceiverMap(lookup_start_object);
3330 TNode<Name> name = CAST(p->name());
3331 TNode<FeedbackVector> vector = CAST(p->vector());
3332 TNode<TaggedIndex> slot = p->slot();
3333 TNode<Context> context = p->context();
3334
3335 // When we get here, we know that the {name} matches the recorded
3336 // feedback name in the {vector} and can safely be used for the
3337 // LoadIC handler logic below.
3338 CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(lookup_start_object_map)));
3339 CSA_ASSERT(this, TaggedEqual(name, LoadFeedbackVectorSlot(vector, slot)),
3340 name, vector);
3341
3342 // Check if we have a matching handler for the {lookup_start_object_map}.
3343 TNode<MaybeObject> feedback_element =
3344 LoadFeedbackVectorSlot(vector, slot, kTaggedSize);
3345 TNode<WeakFixedArray> array = CAST(feedback_element);
3346 HandlePolymorphicCase(lookup_start_object_map, array, &if_handler,
3347 &var_handler, &miss);
3348
3349 BIND(&if_handler);
3350 {
3351 ExitPoint direct_exit(this);
3352 LazyLoadICParameters lazy_p(p);
3353 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
3354 &direct_exit, ICMode::kNonGlobalIC,
3355 OnNonExistent::kReturnUndefined, kOnlyProperties,
3356 access_mode);
3357 }
3358
3359 BIND(&miss);
3360 {
3361 Comment("KeyedLoadIC_miss");
3362 TailCallRuntime(
3363 access_mode == LoadAccessMode::kLoad ? Runtime::kKeyedLoadIC_Miss
3364 : Runtime::kKeyedHasIC_Miss,
3365 context, p->receiver_and_lookup_start_object(), name, slot, vector);
3366 }
3367 }
3368
StoreIC(const StoreICParameters * p)3369 void AccessorAssembler::StoreIC(const StoreICParameters* p) {
3370 TVARIABLE(MaybeObject, var_handler,
3371 ReinterpretCast<MaybeObject>(SmiConstant(0)));
3372
3373 Label if_handler(this, &var_handler),
3374 if_handler_from_stub_cache(this, &var_handler, Label::kDeferred),
3375 try_polymorphic(this, Label::kDeferred),
3376 try_megamorphic(this, Label::kDeferred), miss(this, Label::kDeferred),
3377 no_feedback(this, Label::kDeferred);
3378
3379 TNode<Map> receiver_map = LoadReceiverMap(p->receiver());
3380 GotoIf(IsDeprecatedMap(receiver_map), &miss);
3381
3382 GotoIf(IsUndefined(p->vector()), &no_feedback);
3383
3384 // Check monomorphic case.
3385 TNode<MaybeObject> feedback =
3386 TryMonomorphicCase(p->slot(), CAST(p->vector()), receiver_map,
3387 &if_handler, &var_handler, &try_polymorphic);
3388 BIND(&if_handler);
3389 {
3390 Comment("StoreIC_if_handler");
3391 HandleStoreICHandlerCase(p, var_handler.value(), &miss,
3392 ICMode::kNonGlobalIC);
3393 }
3394
3395 BIND(&try_polymorphic);
3396 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
3397 {
3398 // Check polymorphic case.
3399 Comment("StoreIC_try_polymorphic");
3400 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
3401 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
3402 &var_handler, &miss);
3403 }
3404
3405 BIND(&try_megamorphic);
3406 {
3407 // Check megamorphic case.
3408 GotoIfNot(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()), &miss);
3409
3410 TryProbeStubCache(isolate()->store_stub_cache(), p->receiver(),
3411 CAST(p->name()), &if_handler, &var_handler, &miss);
3412 }
3413
3414 BIND(&no_feedback);
3415 {
3416 TailCallBuiltin(Builtins::kStoreIC_NoFeedback, p->context(), p->receiver(),
3417 p->name(), p->value(), p->slot());
3418 }
3419
3420 BIND(&miss);
3421 {
3422 TailCallRuntime(Runtime::kStoreIC_Miss, p->context(), p->value(), p->slot(),
3423 p->vector(), p->receiver(), p->name());
3424 }
3425 }
3426
StoreGlobalIC(const StoreICParameters * pp)3427 void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) {
3428 Label if_lexical_var(this), if_heapobject(this);
3429 TNode<MaybeObject> maybe_weak_ref =
3430 LoadFeedbackVectorSlot(CAST(pp->vector()), pp->slot());
3431 Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_heapobject);
3432
3433 BIND(&if_heapobject);
3434 {
3435 Label try_handler(this), miss(this, Label::kDeferred);
3436
3437 CSA_ASSERT(this, IsWeakOrCleared(maybe_weak_ref));
3438 TNode<PropertyCell> property_cell =
3439 CAST(GetHeapObjectAssumeWeak(maybe_weak_ref, &try_handler));
3440
3441 ExitPoint direct_exit(this);
3442 StoreGlobalIC_PropertyCellCase(property_cell, pp->value(), &direct_exit,
3443 &miss);
3444
3445 BIND(&try_handler);
3446 {
3447 Comment("StoreGlobalIC_try_handler");
3448 TNode<MaybeObject> handler =
3449 LoadFeedbackVectorSlot(CAST(pp->vector()), pp->slot(), kTaggedSize);
3450
3451 GotoIf(TaggedEqual(handler, UninitializedSymbolConstant()), &miss);
3452
3453 DCHECK(pp->receiver_is_null());
3454 TNode<NativeContext> native_context = LoadNativeContext(pp->context());
3455 StoreICParameters p(
3456 pp->context(),
3457 LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX),
3458 pp->name(), pp->value(), pp->slot(), pp->vector());
3459
3460 HandleStoreICHandlerCase(&p, handler, &miss, ICMode::kGlobalIC);
3461 }
3462
3463 BIND(&miss);
3464 {
3465 TailCallRuntime(Runtime::kStoreGlobalIC_Miss, pp->context(), pp->value(),
3466 pp->slot(), pp->vector(), pp->name());
3467 }
3468 }
3469
3470 BIND(&if_lexical_var);
3471 {
3472 Comment("Store lexical variable");
3473 TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref));
3474 TNode<IntPtrT> context_index =
3475 Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler));
3476 TNode<IntPtrT> slot_index =
3477 Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler));
3478 TNode<Context> script_context =
3479 LoadScriptContext(pp->context(), context_index);
3480 StoreContextElement(script_context, slot_index, pp->value());
3481 Return(pp->value());
3482 }
3483 }
3484
StoreGlobalIC_PropertyCellCase(TNode<PropertyCell> property_cell,TNode<Object> value,ExitPoint * exit_point,Label * miss)3485 void AccessorAssembler::StoreGlobalIC_PropertyCellCase(
3486 TNode<PropertyCell> property_cell, TNode<Object> value,
3487 ExitPoint* exit_point, Label* miss) {
3488 Comment("StoreGlobalIC_TryPropertyCellCase");
3489
3490 // Load the payload of the global parameter cell. A hole indicates that
3491 // the cell has been invalidated and that the store must be handled by the
3492 // runtime.
3493 TNode<Object> cell_contents =
3494 LoadObjectField(property_cell, PropertyCell::kValueOffset);
3495 TNode<Int32T> details = LoadAndUntagToWord32ObjectField(
3496 property_cell, PropertyCell::kPropertyDetailsRawOffset);
3497 GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask), miss);
3498 CSA_ASSERT(this,
3499 Word32Equal(DecodeWord32<PropertyDetails::KindField>(details),
3500 Int32Constant(kData)));
3501
3502 TNode<Uint32T> type =
3503 DecodeWord32<PropertyDetails::PropertyCellTypeField>(details);
3504
3505 Label constant(this), store(this), not_smi(this);
3506
3507 GotoIf(Word32Equal(type, Int32Constant(
3508 static_cast<int>(PropertyCellType::kConstant))),
3509 &constant);
3510
3511 GotoIf(IsTheHole(cell_contents), miss);
3512
3513 GotoIf(Word32Equal(
3514 type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))),
3515 &store);
3516 CSA_ASSERT(this,
3517 Word32Or(Word32Equal(type, Int32Constant(static_cast<int>(
3518 PropertyCellType::kConstantType))),
3519 Word32Equal(type, Int32Constant(static_cast<int>(
3520 PropertyCellType::kUndefined)))));
3521
3522 GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi);
3523 GotoIfNot(TaggedIsSmi(value), miss);
3524 Goto(&store);
3525
3526 BIND(¬_smi);
3527 {
3528 GotoIf(TaggedIsSmi(value), miss);
3529 TNode<Map> expected_map = LoadMap(CAST(cell_contents));
3530 TNode<Map> map = LoadMap(CAST(value));
3531 GotoIfNot(TaggedEqual(expected_map, map), miss);
3532 Goto(&store);
3533 }
3534
3535 BIND(&store);
3536 {
3537 StoreObjectField(property_cell, PropertyCell::kValueOffset, value);
3538 exit_point->Return(value);
3539 }
3540
3541 BIND(&constant);
3542 {
3543 GotoIfNot(TaggedEqual(cell_contents, value), miss);
3544 exit_point->Return(value);
3545 }
3546 }
3547
KeyedStoreIC(const StoreICParameters * p)3548 void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) {
3549 Label miss(this, Label::kDeferred);
3550 {
3551 TVARIABLE(MaybeObject, var_handler);
3552
3553 Label if_handler(this, &var_handler),
3554 try_polymorphic(this, Label::kDeferred),
3555 try_megamorphic(this, Label::kDeferred),
3556 no_feedback(this, Label::kDeferred),
3557 try_polymorphic_name(this, Label::kDeferred);
3558
3559 TNode<Map> receiver_map = LoadReceiverMap(p->receiver());
3560 GotoIf(IsDeprecatedMap(receiver_map), &miss);
3561
3562 GotoIf(IsUndefined(p->vector()), &no_feedback);
3563
3564 // Check monomorphic case.
3565 TNode<MaybeObject> feedback =
3566 TryMonomorphicCase(p->slot(), CAST(p->vector()), receiver_map,
3567 &if_handler, &var_handler, &try_polymorphic);
3568 BIND(&if_handler);
3569 {
3570 Comment("KeyedStoreIC_if_handler");
3571 HandleStoreICHandlerCase(p, var_handler.value(), &miss,
3572 ICMode::kNonGlobalIC, kSupportElements);
3573 }
3574
3575 BIND(&try_polymorphic);
3576 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
3577 {
3578 // CheckPolymorphic case.
3579 Comment("KeyedStoreIC_try_polymorphic");
3580 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)),
3581 &try_megamorphic);
3582 HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
3583 &var_handler, &miss);
3584 }
3585
3586 BIND(&try_megamorphic);
3587 {
3588 // Check megamorphic case.
3589 Comment("KeyedStoreIC_try_megamorphic");
3590 Branch(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()),
3591 &no_feedback, &try_polymorphic_name);
3592 }
3593
3594 BIND(&no_feedback);
3595 {
3596 TailCallBuiltin(Builtins::kKeyedStoreIC_Megamorphic, p->context(),
3597 p->receiver(), p->name(), p->value(), p->slot());
3598 }
3599
3600 BIND(&try_polymorphic_name);
3601 {
3602 // We might have a name in feedback, and a fixed array in the next slot.
3603 Comment("KeyedStoreIC_try_polymorphic_name");
3604 GotoIfNot(TaggedEqual(strong_feedback, p->name()), &miss);
3605 // If the name comparison succeeded, we know we have a feedback vector
3606 // with at least one map/handler pair.
3607 TNode<MaybeObject> feedback_element =
3608 LoadFeedbackVectorSlot(CAST(p->vector()), p->slot(), kTaggedSize);
3609 TNode<WeakFixedArray> array = CAST(feedback_element);
3610 HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler,
3611 &miss);
3612 }
3613 }
3614 BIND(&miss);
3615 {
3616 Comment("KeyedStoreIC_miss");
3617 TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context(), p->value(),
3618 p->slot(), p->vector(), p->receiver(), p->name());
3619 }
3620 }
3621
StoreInArrayLiteralIC(const StoreICParameters * p)3622 void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) {
3623 Label miss(this, Label::kDeferred), no_feedback(this, Label::kDeferred);
3624 {
3625 TVARIABLE(MaybeObject, var_handler);
3626
3627 Label if_handler(this, &var_handler),
3628 try_polymorphic(this, Label::kDeferred),
3629 try_megamorphic(this, Label::kDeferred);
3630
3631 TNode<Map> array_map = LoadReceiverMap(p->receiver());
3632 GotoIf(IsDeprecatedMap(array_map), &miss);
3633
3634 GotoIf(IsUndefined(p->vector()), &no_feedback);
3635
3636 TNode<MaybeObject> feedback =
3637 TryMonomorphicCase(p->slot(), CAST(p->vector()), array_map, &if_handler,
3638 &var_handler, &try_polymorphic);
3639
3640 BIND(&if_handler);
3641 {
3642 Comment("StoreInArrayLiteralIC_if_handler");
3643 // This is a stripped-down version of HandleStoreICHandlerCase.
3644 Label if_transitioning_element_store(this), if_smi_handler(this);
3645
3646 // Check used to identify the Slow case.
3647 // Currently only the Slow case uses a Smi handler.
3648 GotoIf(TaggedIsSmi(var_handler.value()), &if_smi_handler);
3649
3650 TNode<HeapObject> handler = CAST(var_handler.value());
3651 GotoIfNot(IsCode(handler), &if_transitioning_element_store);
3652 TailCallStub(StoreWithVectorDescriptor{}, CAST(handler), p->context(),
3653 p->receiver(), p->name(), p->value(), p->slot(),
3654 p->vector());
3655
3656 BIND(&if_transitioning_element_store);
3657 {
3658 TNode<MaybeObject> maybe_transition_map =
3659 LoadHandlerDataField(CAST(handler), 1);
3660 TNode<Map> transition_map =
3661 CAST(GetHeapObjectAssumeWeak(maybe_transition_map, &miss));
3662 GotoIf(IsDeprecatedMap(transition_map), &miss);
3663 TNode<Code> code =
3664 CAST(LoadObjectField(handler, StoreHandler::kSmiHandlerOffset));
3665 TailCallStub(StoreTransitionDescriptor{}, code, p->context(),
3666 p->receiver(), p->name(), transition_map, p->value(),
3667 p->slot(), p->vector());
3668 }
3669
3670 BIND(&if_smi_handler);
3671 {
3672 #ifdef DEBUG
3673 // A check to ensure that no other Smi handler uses this path.
3674 TNode<Int32T> handler_word = SmiToInt32(CAST(var_handler.value()));
3675 TNode<Uint32T> handler_kind =
3676 DecodeWord32<StoreHandler::KindBits>(handler_word);
3677 CSA_ASSERT(this, Word32Equal(handler_kind,
3678 Int32Constant(StoreHandler::kSlow)));
3679 #endif
3680
3681 Comment("StoreInArrayLiteralIC_Slow");
3682 TailCallRuntime(Runtime::kStoreInArrayLiteralIC_Slow, p->context(),
3683 p->value(), p->receiver(), p->name());
3684 }
3685 }
3686
3687 BIND(&try_polymorphic);
3688 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
3689 {
3690 Comment("StoreInArrayLiteralIC_try_polymorphic");
3691 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)),
3692 &try_megamorphic);
3693 HandlePolymorphicCase(array_map, CAST(strong_feedback), &if_handler,
3694 &var_handler, &miss);
3695 }
3696
3697 BIND(&try_megamorphic);
3698 {
3699 Comment("StoreInArrayLiteralIC_try_megamorphic");
3700 CSA_ASSERT(
3701 this,
3702 Word32Or(TaggedEqual(strong_feedback, UninitializedSymbolConstant()),
3703 TaggedEqual(strong_feedback, MegamorphicSymbolConstant())));
3704 GotoIfNot(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()),
3705 &miss);
3706 TailCallRuntime(Runtime::kStoreInArrayLiteralIC_Slow, p->context(),
3707 p->value(), p->receiver(), p->name());
3708 }
3709 }
3710
3711 BIND(&no_feedback);
3712 {
3713 Comment("StoreInArrayLiteralIC_NoFeedback");
3714 TailCallBuiltin(Builtins::kSetPropertyInLiteral, p->context(),
3715 p->receiver(), p->name(), p->value());
3716 }
3717
3718 BIND(&miss);
3719 {
3720 Comment("StoreInArrayLiteralIC_miss");
3721 TailCallRuntime(Runtime::kStoreInArrayLiteralIC_Miss, p->context(),
3722 p->value(), p->slot(), p->vector(), p->receiver(),
3723 p->name());
3724 }
3725 }
3726
3727 //////////////////// Public methods.
3728
GenerateLoadIC()3729 void AccessorAssembler::GenerateLoadIC() {
3730 using Descriptor = LoadWithVectorDescriptor;
3731
3732 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3733 auto name = Parameter<Object>(Descriptor::kName);
3734 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3735 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3736 auto context = Parameter<Context>(Descriptor::kContext);
3737
3738 LoadICParameters p(context, receiver, name, slot, vector);
3739 LoadIC(&p);
3740 }
3741
GenerateLoadIC_Megamorphic()3742 void AccessorAssembler::GenerateLoadIC_Megamorphic() {
3743 using Descriptor = LoadWithVectorDescriptor;
3744
3745 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3746 auto name = Parameter<Object>(Descriptor::kName);
3747 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3748 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3749 auto context = Parameter<Context>(Descriptor::kContext);
3750
3751 ExitPoint direct_exit(this);
3752 TVARIABLE(MaybeObject, var_handler);
3753 Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
3754
3755 CSA_ASSERT(this, TaggedEqual(LoadFeedbackVectorSlot(CAST(vector), slot),
3756 MegamorphicSymbolConstant()));
3757
3758 TryProbeStubCache(isolate()->load_stub_cache(), receiver, CAST(name),
3759 &if_handler, &var_handler, &miss);
3760
3761 BIND(&if_handler);
3762 LazyLoadICParameters p(
3763 // lazy_context
3764 [=] { return context; }, receiver,
3765 // lazy_name
3766 [=] { return name; },
3767 // lazy_slot
3768 [=] { return slot; }, vector);
3769 HandleLoadICHandlerCase(&p, CAST(var_handler.value()), &miss, &direct_exit);
3770
3771 BIND(&miss);
3772 direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name,
3773 slot, vector);
3774 }
3775
GenerateLoadIC_Noninlined()3776 void AccessorAssembler::GenerateLoadIC_Noninlined() {
3777 using Descriptor = LoadWithVectorDescriptor;
3778
3779 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3780 auto name = Parameter<Object>(Descriptor::kName);
3781 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3782 auto vector = Parameter<FeedbackVector>(Descriptor::kVector);
3783 auto context = Parameter<Context>(Descriptor::kContext);
3784
3785 ExitPoint direct_exit(this);
3786 TVARIABLE(MaybeObject, var_handler);
3787 Label if_handler(this, &var_handler), miss(this, Label::kDeferred);
3788
3789 TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(vector, slot);
3790 TNode<HeapObject> feedback = CAST(feedback_element);
3791
3792 LoadICParameters p(context, receiver, name, slot, vector);
3793 TNode<Map> lookup_start_object_map = LoadReceiverMap(p.lookup_start_object());
3794 LoadIC_Noninlined(&p, lookup_start_object_map, feedback, &var_handler,
3795 &if_handler, &miss, &direct_exit);
3796
3797 BIND(&if_handler);
3798 {
3799 LazyLoadICParameters lazy_p(&p);
3800 HandleLoadICHandlerCase(&lazy_p, CAST(var_handler.value()), &miss,
3801 &direct_exit);
3802 }
3803
3804 BIND(&miss);
3805 direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name,
3806 slot, vector);
3807 }
3808
GenerateLoadIC_NoFeedback()3809 void AccessorAssembler::GenerateLoadIC_NoFeedback() {
3810 using Descriptor = LoadNoFeedbackDescriptor;
3811
3812 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3813 auto name = Parameter<Object>(Descriptor::kName);
3814 auto context = Parameter<Context>(Descriptor::kContext);
3815 auto ic_kind = Parameter<Smi>(Descriptor::kICKind);
3816
3817 LoadICParameters p(context, receiver, name,
3818 TaggedIndexConstant(FeedbackSlot::Invalid().ToInt()),
3819 UndefinedConstant());
3820 LoadIC_NoFeedback(&p, ic_kind);
3821 }
3822
GenerateLoadICTrampoline()3823 void AccessorAssembler::GenerateLoadICTrampoline() {
3824 using Descriptor = LoadDescriptor;
3825
3826 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3827 auto name = Parameter<Object>(Descriptor::kName);
3828 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3829 auto context = Parameter<Context>(Descriptor::kContext);
3830 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3831
3832 TailCallBuiltin(Builtins::kLoadIC, context, receiver, name, slot, vector);
3833 }
3834
GenerateLoadICTrampoline_Megamorphic()3835 void AccessorAssembler::GenerateLoadICTrampoline_Megamorphic() {
3836 using Descriptor = LoadDescriptor;
3837
3838 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3839 auto name = Parameter<Object>(Descriptor::kName);
3840 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3841 auto context = Parameter<Context>(Descriptor::kContext);
3842 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3843
3844 TailCallBuiltin(Builtins::kLoadIC_Megamorphic, context, receiver, name, slot,
3845 vector);
3846 }
3847
GenerateLoadSuperIC()3848 void AccessorAssembler::GenerateLoadSuperIC() {
3849 using Descriptor = LoadWithReceiverAndVectorDescriptor;
3850
3851 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3852 auto lookup_start_object = Parameter<Object>(Descriptor::kLookupStartObject);
3853 auto name = Parameter<Object>(Descriptor::kName);
3854 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3855 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3856 auto context = Parameter<Context>(Descriptor::kContext);
3857
3858 LoadICParameters p(context, receiver, name, slot, vector,
3859 lookup_start_object);
3860 LoadSuperIC(&p);
3861 }
3862
GenerateLoadGlobalIC_NoFeedback()3863 void AccessorAssembler::GenerateLoadGlobalIC_NoFeedback() {
3864 using Descriptor = LoadGlobalNoFeedbackDescriptor;
3865
3866 auto name = Parameter<Object>(Descriptor::kName);
3867 auto context = Parameter<Context>(Descriptor::kContext);
3868 auto ic_kind = Parameter<Smi>(Descriptor::kICKind);
3869
3870 LoadGlobalIC_NoFeedback(context, name, ic_kind);
3871 }
3872
GenerateLoadGlobalIC(TypeofMode typeof_mode)3873 void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) {
3874 using Descriptor = LoadGlobalWithVectorDescriptor;
3875
3876 auto name = Parameter<Name>(Descriptor::kName);
3877 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3878 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3879 auto context = Parameter<Context>(Descriptor::kContext);
3880
3881 ExitPoint direct_exit(this);
3882 LoadGlobalIC(
3883 vector,
3884 // lazy_slot
3885 [=] { return slot; },
3886 // lazy_context
3887 [=] { return context; },
3888 // lazy_name
3889 [=] { return name; }, typeof_mode, &direct_exit);
3890 }
3891
GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode)3892 void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) {
3893 using Descriptor = LoadGlobalDescriptor;
3894
3895 auto name = Parameter<Object>(Descriptor::kName);
3896 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3897 auto context = Parameter<Context>(Descriptor::kContext);
3898 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3899
3900 Callable callable =
3901 CodeFactory::LoadGlobalICInOptimizedCode(isolate(), typeof_mode);
3902 TailCallStub(callable, context, name, slot, vector);
3903 }
3904
GenerateKeyedLoadIC()3905 void AccessorAssembler::GenerateKeyedLoadIC() {
3906 using Descriptor = LoadWithVectorDescriptor;
3907
3908 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3909 auto name = Parameter<Object>(Descriptor::kName);
3910 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3911 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3912 auto context = Parameter<Context>(Descriptor::kContext);
3913
3914 LoadICParameters p(context, receiver, name, slot, vector);
3915 KeyedLoadIC(&p, LoadAccessMode::kLoad);
3916 }
3917
GenerateKeyedLoadIC_Megamorphic()3918 void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() {
3919 using Descriptor = LoadWithVectorDescriptor;
3920
3921 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3922 auto name = Parameter<Object>(Descriptor::kName);
3923 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3924 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3925 auto context = Parameter<Context>(Descriptor::kContext);
3926
3927 LoadICParameters p(context, receiver, name, slot, vector);
3928 KeyedLoadICGeneric(&p);
3929 }
3930
GenerateKeyedLoadICTrampoline()3931 void AccessorAssembler::GenerateKeyedLoadICTrampoline() {
3932 using Descriptor = LoadDescriptor;
3933
3934 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3935 auto name = Parameter<Object>(Descriptor::kName);
3936 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3937 auto context = Parameter<Context>(Descriptor::kContext);
3938 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3939
3940 TailCallBuiltin(Builtins::kKeyedLoadIC, context, receiver, name, slot,
3941 vector);
3942 }
3943
GenerateKeyedLoadICTrampoline_Megamorphic()3944 void AccessorAssembler::GenerateKeyedLoadICTrampoline_Megamorphic() {
3945 using Descriptor = LoadDescriptor;
3946
3947 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3948 auto name = Parameter<Object>(Descriptor::kName);
3949 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3950 auto context = Parameter<Context>(Descriptor::kContext);
3951 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3952
3953 TailCallBuiltin(Builtins::kKeyedLoadIC_Megamorphic, context, receiver, name,
3954 slot, vector);
3955 }
3956
GenerateKeyedLoadIC_PolymorphicName()3957 void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() {
3958 using Descriptor = LoadWithVectorDescriptor;
3959
3960 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3961 auto name = Parameter<Object>(Descriptor::kName);
3962 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3963 auto vector = Parameter<FeedbackVector>(Descriptor::kVector);
3964 auto context = Parameter<Context>(Descriptor::kContext);
3965
3966 LoadICParameters p(context, receiver, name, slot, vector);
3967 KeyedLoadICPolymorphicName(&p, LoadAccessMode::kLoad);
3968 }
3969
GenerateStoreGlobalIC()3970 void AccessorAssembler::GenerateStoreGlobalIC() {
3971 using Descriptor = StoreGlobalWithVectorDescriptor;
3972
3973 auto name = Parameter<Object>(Descriptor::kName);
3974 auto value = Parameter<Object>(Descriptor::kValue);
3975 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3976 auto vector = Parameter<HeapObject>(Descriptor::kVector);
3977 auto context = Parameter<Context>(Descriptor::kContext);
3978
3979 StoreICParameters p(context, base::nullopt, name, value, slot, vector);
3980 StoreGlobalIC(&p);
3981 }
3982
GenerateStoreGlobalICTrampoline()3983 void AccessorAssembler::GenerateStoreGlobalICTrampoline() {
3984 using Descriptor = StoreGlobalDescriptor;
3985
3986 auto name = Parameter<Object>(Descriptor::kName);
3987 auto value = Parameter<Object>(Descriptor::kValue);
3988 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
3989 auto context = Parameter<Context>(Descriptor::kContext);
3990 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
3991
3992 TailCallBuiltin(Builtins::kStoreGlobalIC, context, name, value, slot, vector);
3993 }
3994
GenerateStoreIC()3995 void AccessorAssembler::GenerateStoreIC() {
3996 using Descriptor = StoreWithVectorDescriptor;
3997
3998 auto receiver = Parameter<Object>(Descriptor::kReceiver);
3999 auto name = Parameter<Object>(Descriptor::kName);
4000 auto value = Parameter<Object>(Descriptor::kValue);
4001 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4002 auto vector = Parameter<HeapObject>(Descriptor::kVector);
4003 auto context = Parameter<Context>(Descriptor::kContext);
4004
4005 StoreICParameters p(context, receiver, name, value, slot, vector);
4006 StoreIC(&p);
4007 }
4008
GenerateStoreICTrampoline()4009 void AccessorAssembler::GenerateStoreICTrampoline() {
4010 using Descriptor = StoreDescriptor;
4011
4012 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4013 auto name = Parameter<Object>(Descriptor::kName);
4014 auto value = Parameter<Object>(Descriptor::kValue);
4015 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4016 auto context = Parameter<Context>(Descriptor::kContext);
4017 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
4018
4019 TailCallBuiltin(Builtins::kStoreIC, context, receiver, name, value, slot,
4020 vector);
4021 }
4022
GenerateKeyedStoreIC()4023 void AccessorAssembler::GenerateKeyedStoreIC() {
4024 using Descriptor = StoreWithVectorDescriptor;
4025
4026 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4027 auto name = Parameter<Object>(Descriptor::kName);
4028 auto value = Parameter<Object>(Descriptor::kValue);
4029 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4030 auto vector = Parameter<HeapObject>(Descriptor::kVector);
4031 auto context = Parameter<Context>(Descriptor::kContext);
4032
4033 StoreICParameters p(context, receiver, name, value, slot, vector);
4034 KeyedStoreIC(&p);
4035 }
4036
GenerateKeyedStoreICTrampoline()4037 void AccessorAssembler::GenerateKeyedStoreICTrampoline() {
4038 using Descriptor = StoreDescriptor;
4039
4040 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4041 auto name = Parameter<Object>(Descriptor::kName);
4042 auto value = Parameter<Object>(Descriptor::kValue);
4043 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4044 auto context = Parameter<Context>(Descriptor::kContext);
4045 TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
4046
4047 TailCallBuiltin(Builtins::kKeyedStoreIC, context, receiver, name, value, slot,
4048 vector);
4049 }
4050
GenerateStoreInArrayLiteralIC()4051 void AccessorAssembler::GenerateStoreInArrayLiteralIC() {
4052 using Descriptor = StoreWithVectorDescriptor;
4053
4054 auto array = Parameter<Object>(Descriptor::kReceiver);
4055 auto index = Parameter<Object>(Descriptor::kName);
4056 auto value = Parameter<Object>(Descriptor::kValue);
4057 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4058 auto vector = Parameter<HeapObject>(Descriptor::kVector);
4059 auto context = Parameter<Context>(Descriptor::kContext);
4060
4061 StoreICParameters p(context, array, index, value, slot, vector);
4062 StoreInArrayLiteralIC(&p);
4063 }
4064
GenerateCloneObjectIC_Slow()4065 void AccessorAssembler::GenerateCloneObjectIC_Slow() {
4066 using Descriptor = CloneObjectWithVectorDescriptor;
4067 auto source = Parameter<Object>(Descriptor::kSource);
4068 auto flags = Parameter<Smi>(Descriptor::kFlags);
4069 auto context = Parameter<Context>(Descriptor::kContext);
4070
4071 // The Slow case uses the same call interface as CloneObjectIC, so that it
4072 // can be tail called from it. However, the feedback slot and vector are not
4073 // used.
4074
4075 TNode<NativeContext> native_context = LoadNativeContext(context);
4076 TNode<Map> initial_map = LoadObjectFunctionInitialMap(native_context);
4077 TNode<JSObject> result = AllocateJSObjectFromMap(initial_map);
4078
4079 {
4080 Label did_set_proto_if_needed(this);
4081 TNode<BoolT> is_null_proto = SmiNotEqual(
4082 SmiAnd(flags, SmiConstant(ObjectLiteral::kHasNullPrototype)),
4083 SmiConstant(Smi::zero()));
4084 GotoIfNot(is_null_proto, &did_set_proto_if_needed);
4085
4086 CallRuntime(Runtime::kInternalSetPrototype, context, result,
4087 NullConstant());
4088
4089 Goto(&did_set_proto_if_needed);
4090 BIND(&did_set_proto_if_needed);
4091 }
4092
4093 ReturnIf(IsNullOrUndefined(source), result);
4094 source = ToObject_Inline(context, source);
4095
4096 Label call_runtime(this, Label::kDeferred), done(this);
4097
4098 TNode<Map> source_map = LoadMap(CAST(source));
4099 GotoIfNot(IsJSObjectMap(source_map), &call_runtime);
4100 GotoIfNot(IsEmptyFixedArray(LoadElements(CAST(source))), &call_runtime);
4101
4102 ForEachEnumerableOwnProperty(
4103 context, source_map, CAST(source), kPropertyAdditionOrder,
4104 [=](TNode<Name> key, TNode<Object> value) {
4105 SetPropertyInLiteral(context, result, key, value);
4106 },
4107 &call_runtime);
4108 Goto(&done);
4109
4110 BIND(&call_runtime);
4111 CallRuntime(Runtime::kCopyDataProperties, context, result, source);
4112
4113 Goto(&done);
4114 BIND(&done);
4115 Return(result);
4116 }
4117
GenerateCloneObjectIC()4118 void AccessorAssembler::GenerateCloneObjectIC() {
4119 using Descriptor = CloneObjectWithVectorDescriptor;
4120 auto source = Parameter<Object>(Descriptor::kSource);
4121 auto flags = Parameter<Smi>(Descriptor::kFlags);
4122 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4123 auto maybe_vector = Parameter<HeapObject>(Descriptor::kVector);
4124 auto context = Parameter<Context>(Descriptor::kContext);
4125 TVARIABLE(MaybeObject, var_handler);
4126 Label if_handler(this, &var_handler), miss(this, Label::kDeferred),
4127 try_polymorphic(this, Label::kDeferred),
4128 try_megamorphic(this, Label::kDeferred), slow(this, Label::kDeferred);
4129
4130 TNode<Map> source_map = LoadReceiverMap(source);
4131 GotoIf(IsDeprecatedMap(source_map), &miss);
4132
4133 GotoIf(IsUndefined(maybe_vector), &slow);
4134
4135 TNode<MaybeObject> feedback =
4136 TryMonomorphicCase(slot, CAST(maybe_vector), source_map, &if_handler,
4137 &var_handler, &try_polymorphic);
4138
4139 BIND(&if_handler);
4140 {
4141 Comment("CloneObjectIC_if_handler");
4142
4143 // Handlers for the CloneObjectIC stub are weak references to the Map of
4144 // a result object.
4145 TNode<Map> result_map = CAST(var_handler.value());
4146 TVARIABLE(HeapObject, var_properties, EmptyFixedArrayConstant());
4147 TVARIABLE(FixedArray, var_elements, EmptyFixedArrayConstant());
4148
4149 Label allocate_object(this);
4150 GotoIf(IsNullOrUndefined(source), &allocate_object);
4151 CSA_SLOW_ASSERT(this, IsJSObjectMap(source_map));
4152 CSA_SLOW_ASSERT(this, IsJSObjectMap(result_map));
4153
4154 // The IC fast case should only be taken if the result map a compatible
4155 // elements kind with the source object.
4156 TNode<FixedArrayBase> source_elements = LoadElements(CAST(source));
4157
4158 auto flags = ExtractFixedArrayFlag::kAllFixedArraysDontCopyCOW;
4159 var_elements = CAST(CloneFixedArray(source_elements, flags));
4160
4161 // Copy the PropertyArray backing store. The source PropertyArray must be
4162 // either an Smi, or a PropertyArray.
4163 // FIXME: Make a CSA macro for this
4164 TNode<Object> source_properties =
4165 LoadObjectField(CAST(source), JSObject::kPropertiesOrHashOffset);
4166 {
4167 GotoIf(TaggedIsSmi(source_properties), &allocate_object);
4168 GotoIf(IsEmptyFixedArray(source_properties), &allocate_object);
4169
4170 // This IC requires that the source object has fast properties.
4171 TNode<PropertyArray> source_property_array = CAST(source_properties);
4172
4173 TNode<IntPtrT> length = LoadPropertyArrayLength(source_property_array);
4174 GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &allocate_object);
4175
4176 TNode<PropertyArray> property_array = AllocatePropertyArray(length);
4177 FillPropertyArrayWithUndefined(property_array, IntPtrConstant(0), length);
4178 CopyPropertyArrayValues(source_property_array, property_array, length,
4179 SKIP_WRITE_BARRIER, DestroySource::kNo);
4180 var_properties = property_array;
4181 }
4182
4183 Goto(&allocate_object);
4184 BIND(&allocate_object);
4185 TNode<JSObject> object = UncheckedCast<JSObject>(AllocateJSObjectFromMap(
4186 result_map, var_properties.value(), var_elements.value()));
4187 ReturnIf(IsNullOrUndefined(source), object);
4188
4189 // Lastly, clone any in-object properties.
4190 TNode<IntPtrT> source_start =
4191 LoadMapInobjectPropertiesStartInWords(source_map);
4192 TNode<IntPtrT> source_size = LoadMapInstanceSizeInWords(source_map);
4193 TNode<IntPtrT> result_start =
4194 LoadMapInobjectPropertiesStartInWords(result_map);
4195 TNode<IntPtrT> field_offset_difference =
4196 TimesTaggedSize(IntPtrSub(result_start, source_start));
4197
4198 // Just copy the fields as raw data (pretending that there are no mutable
4199 // HeapNumbers). This doesn't need write barriers.
4200 BuildFastLoop<IntPtrT>(
4201 source_start, source_size,
4202 [=](TNode<IntPtrT> field_index) {
4203 TNode<IntPtrT> field_offset = TimesTaggedSize(field_index);
4204 TNode<TaggedT> field =
4205 LoadObjectField<TaggedT>(CAST(source), field_offset);
4206 TNode<IntPtrT> result_offset =
4207 IntPtrAdd(field_offset, field_offset_difference);
4208 StoreObjectFieldNoWriteBarrier(object, result_offset, field);
4209 },
4210 1, IndexAdvanceMode::kPost);
4211
4212 // If mutable HeapNumbers can occur, we need to go through the {object}
4213 // again here and properly clone them. We use a second loop here to
4214 // ensure that the GC (and heap verifier) always sees properly initialized
4215 // objects, i.e. never hits undefined values in double fields.
4216 if (!FLAG_unbox_double_fields) {
4217 TNode<IntPtrT> start_offset = TimesTaggedSize(result_start);
4218 TNode<IntPtrT> end_offset =
4219 IntPtrAdd(TimesTaggedSize(source_size), field_offset_difference);
4220 ConstructorBuiltinsAssembler(state()).CopyMutableHeapNumbersInObject(
4221 object, start_offset, end_offset);
4222 }
4223
4224 Return(object);
4225 }
4226
4227 BIND(&try_polymorphic);
4228 TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
4229 {
4230 Comment("CloneObjectIC_try_polymorphic");
4231 GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic);
4232 HandlePolymorphicCase(source_map, CAST(strong_feedback), &if_handler,
4233 &var_handler, &miss);
4234 }
4235
4236 BIND(&try_megamorphic);
4237 {
4238 Comment("CloneObjectIC_try_megamorphic");
4239 CSA_ASSERT(
4240 this,
4241 Word32Or(TaggedEqual(strong_feedback, UninitializedSymbolConstant()),
4242 TaggedEqual(strong_feedback, MegamorphicSymbolConstant())));
4243 GotoIfNot(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()), &miss);
4244 Goto(&slow);
4245 }
4246
4247 BIND(&slow);
4248 {
4249 TailCallBuiltin(Builtins::kCloneObjectIC_Slow, context, source, flags, slot,
4250 maybe_vector);
4251 }
4252
4253 BIND(&miss);
4254 {
4255 Comment("CloneObjectIC_miss");
4256 TNode<HeapObject> map_or_result =
4257 CAST(CallRuntime(Runtime::kCloneObjectIC_Miss, context, source, flags,
4258 slot, maybe_vector));
4259 var_handler = UncheckedCast<MaybeObject>(map_or_result);
4260 GotoIf(IsMap(map_or_result), &if_handler);
4261 CSA_ASSERT(this, IsJSObject(map_or_result));
4262 Return(map_or_result);
4263 }
4264 }
4265
GenerateKeyedHasIC()4266 void AccessorAssembler::GenerateKeyedHasIC() {
4267 using Descriptor = LoadWithVectorDescriptor;
4268
4269 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4270 auto name = Parameter<Object>(Descriptor::kName);
4271 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4272 auto vector = Parameter<HeapObject>(Descriptor::kVector);
4273 auto context = Parameter<Context>(Descriptor::kContext);
4274
4275 LoadICParameters p(context, receiver, name, slot, vector);
4276 KeyedLoadIC(&p, LoadAccessMode::kHas);
4277 }
4278
GenerateKeyedHasIC_Megamorphic()4279 void AccessorAssembler::GenerateKeyedHasIC_Megamorphic() {
4280 using Descriptor = LoadWithVectorDescriptor;
4281
4282 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4283 auto name = Parameter<Object>(Descriptor::kName);
4284 auto context = Parameter<Context>(Descriptor::kContext);
4285 // TODO(magardn): implement HasProperty handling in KeyedLoadICGeneric
4286 Return(HasProperty(context, receiver, name,
4287 HasPropertyLookupMode::kHasProperty));
4288 }
4289
GenerateKeyedHasIC_PolymorphicName()4290 void AccessorAssembler::GenerateKeyedHasIC_PolymorphicName() {
4291 using Descriptor = LoadWithVectorDescriptor;
4292
4293 auto receiver = Parameter<Object>(Descriptor::kReceiver);
4294 auto name = Parameter<Object>(Descriptor::kName);
4295 auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
4296 auto vector = Parameter<HeapObject>(Descriptor::kVector);
4297 auto context = Parameter<Context>(Descriptor::kContext);
4298
4299 LoadICParameters p(context, receiver, name, slot, vector);
4300 KeyedLoadICPolymorphicName(&p, LoadAccessMode::kHas);
4301 }
4302
BranchIfPrototypesHaveNoElements(TNode<Map> receiver_map,Label * definitely_no_elements,Label * possibly_elements)4303 void AccessorAssembler::BranchIfPrototypesHaveNoElements(
4304 TNode<Map> receiver_map, Label* definitely_no_elements,
4305 Label* possibly_elements) {
4306 TVARIABLE(Map, var_map, receiver_map);
4307 Label loop_body(this, &var_map);
4308 TNode<FixedArray> empty_fixed_array = EmptyFixedArrayConstant();
4309 TNode<NumberDictionary> empty_slow_element_dictionary =
4310 EmptySlowElementDictionaryConstant();
4311 Goto(&loop_body);
4312
4313 BIND(&loop_body);
4314 {
4315 TNode<Map> map = var_map.value();
4316 TNode<HeapObject> prototype = LoadMapPrototype(map);
4317 GotoIf(IsNull(prototype), definitely_no_elements);
4318 TNode<Map> prototype_map = LoadMap(prototype);
4319 TNode<Uint16T> prototype_instance_type = LoadMapInstanceType(prototype_map);
4320
4321 // Pessimistically assume elements if a Proxy, Special API Object,
4322 // or JSPrimitiveWrapper wrapper is found on the prototype chain. After this
4323 // instance type check, it's not necessary to check for interceptors or
4324 // access checks.
4325 Label if_custom(this, Label::kDeferred), if_notcustom(this);
4326 Branch(IsCustomElementsReceiverInstanceType(prototype_instance_type),
4327 &if_custom, &if_notcustom);
4328
4329 BIND(&if_custom);
4330 {
4331 // For string JSPrimitiveWrapper wrappers we still support the checks as
4332 // long as they wrap the empty string.
4333 GotoIfNot(
4334 InstanceTypeEqual(prototype_instance_type, JS_PRIMITIVE_WRAPPER_TYPE),
4335 possibly_elements);
4336 TNode<Object> prototype_value =
4337 LoadJSPrimitiveWrapperValue(CAST(prototype));
4338 Branch(IsEmptyString(prototype_value), &if_notcustom, possibly_elements);
4339 }
4340
4341 BIND(&if_notcustom);
4342 {
4343 TNode<FixedArrayBase> prototype_elements = LoadElements(CAST(prototype));
4344 var_map = prototype_map;
4345 GotoIf(TaggedEqual(prototype_elements, empty_fixed_array), &loop_body);
4346 Branch(TaggedEqual(prototype_elements, empty_slow_element_dictionary),
4347 &loop_body, possibly_elements);
4348 }
4349 }
4350 }
4351
4352 } // namespace internal
4353 } // namespace v8
4354