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