1 // Copyright 2013 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 #if V8_TARGET_ARCH_ARM64 6 7 #include "src/codegen.h" 8 #include "src/ic/ic.h" 9 #include "src/ic/ic-compiler.h" 10 #include "src/ic/stub-cache.h" 11 12 namespace v8 { 13 namespace internal { 14 15 16 #define __ ACCESS_MASM(masm) 17 18 19 // "type" holds an instance type on entry and is not clobbered. 20 // Generated code branch on "global_object" if type is any kind of global 21 // JS object. 22 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type, 23 Label* global_object) { 24 __ Cmp(type, JS_GLOBAL_OBJECT_TYPE); 25 __ Ccmp(type, JS_GLOBAL_PROXY_TYPE, ZFlag, ne); 26 __ B(eq, global_object); 27 } 28 29 30 // Helper function used from LoadIC GenerateNormal. 31 // 32 // elements: Property dictionary. It is not clobbered if a jump to the miss 33 // label is done. 34 // name: Property name. It is not clobbered if a jump to the miss label is 35 // done 36 // result: Register for the result. It is only updated if a jump to the miss 37 // label is not done. 38 // The scratch registers need to be different from elements, name and result. 39 // The generated code assumes that the receiver has slow properties, 40 // is not a global object and does not have interceptors. 41 static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss, 42 Register elements, Register name, 43 Register result, Register scratch1, 44 Register scratch2) { 45 DCHECK(!AreAliased(elements, name, scratch1, scratch2)); 46 DCHECK(!AreAliased(result, scratch1, scratch2)); 47 48 Label done; 49 50 // Probe the dictionary. 51 NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements, 52 name, scratch1, scratch2); 53 54 // If probing finds an entry check that the value is a normal property. 55 __ Bind(&done); 56 57 static const int kElementsStartOffset = 58 NameDictionary::kHeaderSize + 59 NameDictionary::kElementsStartIndex * kPointerSize; 60 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; 61 __ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); 62 __ Tst(scratch1, Smi::FromInt(PropertyDetails::TypeField::kMask)); 63 __ B(ne, miss); 64 65 // Get the value at the masked, scaled index and return. 66 __ Ldr(result, 67 FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize)); 68 } 69 70 71 // Helper function used from StoreIC::GenerateNormal. 72 // 73 // elements: Property dictionary. It is not clobbered if a jump to the miss 74 // label is done. 75 // name: Property name. It is not clobbered if a jump to the miss label is 76 // done 77 // value: The value to store (never clobbered). 78 // 79 // The generated code assumes that the receiver has slow properties, 80 // is not a global object and does not have interceptors. 81 static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss, 82 Register elements, Register name, 83 Register value, Register scratch1, 84 Register scratch2) { 85 DCHECK(!AreAliased(elements, name, value, scratch1, scratch2)); 86 87 Label done; 88 89 // Probe the dictionary. 90 NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements, 91 name, scratch1, scratch2); 92 93 // If probing finds an entry in the dictionary check that the value 94 // is a normal property that is not read only. 95 __ Bind(&done); 96 97 static const int kElementsStartOffset = 98 NameDictionary::kHeaderSize + 99 NameDictionary::kElementsStartIndex * kPointerSize; 100 static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; 101 static const int kTypeAndReadOnlyMask = 102 PropertyDetails::TypeField::kMask | 103 PropertyDetails::AttributesField::encode(READ_ONLY); 104 __ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset)); 105 __ Tst(scratch1, kTypeAndReadOnlyMask); 106 __ B(ne, miss); 107 108 // Store the value at the masked, scaled index and return. 109 static const int kValueOffset = kElementsStartOffset + kPointerSize; 110 __ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag); 111 __ Str(value, MemOperand(scratch2)); 112 113 // Update the write barrier. Make sure not to clobber the value. 114 __ Mov(scratch1, value); 115 __ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved, 116 kDontSaveFPRegs); 117 } 118 119 120 // Checks the receiver for special cases (value type, slow case bits). 121 // Falls through for regular JS object and return the map of the 122 // receiver in 'map_scratch' if the receiver is not a SMI. 123 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, 124 Register receiver, 125 Register map_scratch, 126 Register scratch, 127 int interceptor_bit, Label* slow) { 128 DCHECK(!AreAliased(map_scratch, scratch)); 129 130 // Check that the object isn't a smi. 131 __ JumpIfSmi(receiver, slow); 132 // Get the map of the receiver. 133 __ Ldr(map_scratch, FieldMemOperand(receiver, HeapObject::kMapOffset)); 134 // Check bit field. 135 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kBitFieldOffset)); 136 __ Tbnz(scratch, Map::kIsAccessCheckNeeded, slow); 137 __ Tbnz(scratch, interceptor_bit, slow); 138 139 // Check that the object is some kind of JS object EXCEPT JS Value type. 140 // In the case that the object is a value-wrapper object, we enter the 141 // runtime system to make sure that indexing into string objects work 142 // as intended. 143 STATIC_ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); 144 __ Ldrb(scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); 145 __ Cmp(scratch, JS_OBJECT_TYPE); 146 __ B(lt, slow); 147 } 148 149 150 // Loads an indexed element from a fast case array. 151 // 152 // receiver - holds the receiver on entry. 153 // Unchanged unless 'result' is the same register. 154 // 155 // key - holds the smi key on entry. 156 // Unchanged unless 'result' is the same register. 157 // 158 // elements - holds the elements of the receiver and its prototypes. Clobbered. 159 // 160 // result - holds the result on exit if the load succeeded. 161 // Allowed to be the the same as 'receiver' or 'key'. 162 // Unchanged on bailout so 'receiver' and 'key' can be safely 163 // used by further computation. 164 static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver, 165 Register key, Register elements, 166 Register scratch1, Register scratch2, 167 Register result, Label* slow, 168 LanguageMode language_mode) { 169 DCHECK(!AreAliased(receiver, key, elements, scratch1, scratch2)); 170 171 Label check_prototypes, check_next_prototype; 172 Label done, in_bounds, absent; 173 174 // Check for fast array. 175 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 176 __ AssertFastElements(elements); 177 178 // Check that the key (index) is within bounds. 179 __ Ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset)); 180 __ Cmp(key, scratch1); 181 __ B(lo, &in_bounds); 182 183 // Out of bounds. Check the prototype chain to see if we can just return 184 // 'undefined'. 185 __ Cmp(key, Operand(Smi::FromInt(0))); 186 __ B(lt, slow); // Negative keys can't take the fast OOB path. 187 __ Bind(&check_prototypes); 188 __ Ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset)); 189 __ Bind(&check_next_prototype); 190 __ Ldr(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset)); 191 // scratch2: current prototype 192 __ JumpIfRoot(scratch2, Heap::kNullValueRootIndex, &absent); 193 __ Ldr(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset)); 194 __ Ldr(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset)); 195 // elements: elements of current prototype 196 // scratch2: map of current prototype 197 __ CompareInstanceType(scratch2, scratch1, JS_OBJECT_TYPE); 198 __ B(lo, slow); 199 __ Ldrb(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset)); 200 __ Tbnz(scratch1, Map::kIsAccessCheckNeeded, slow); 201 __ Tbnz(scratch1, Map::kHasIndexedInterceptor, slow); 202 __ JumpIfNotRoot(elements, Heap::kEmptyFixedArrayRootIndex, slow); 203 __ B(&check_next_prototype); 204 205 __ Bind(&absent); 206 if (is_strong(language_mode)) { 207 // Strong mode accesses must throw in this case, so call the runtime. 208 __ B(slow); 209 } else { 210 __ LoadRoot(result, Heap::kUndefinedValueRootIndex); 211 __ B(&done); 212 } 213 214 __ Bind(&in_bounds); 215 // Fast case: Do the load. 216 __ Add(scratch1, elements, FixedArray::kHeaderSize - kHeapObjectTag); 217 __ SmiUntag(scratch2, key); 218 __ Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); 219 220 // In case the loaded value is the_hole we have to check the prototype chain. 221 __ JumpIfRoot(scratch2, Heap::kTheHoleValueRootIndex, &check_prototypes); 222 223 // Move the value to the result register. 224 // 'result' can alias with 'receiver' or 'key' but these two must be 225 // preserved if we jump to 'slow'. 226 __ Mov(result, scratch2); 227 __ Bind(&done); 228 } 229 230 231 // Checks whether a key is an array index string or a unique name. 232 // Falls through if a key is a unique name. 233 // The map of the key is returned in 'map_scratch'. 234 // If the jump to 'index_string' is done the hash of the key is left 235 // in 'hash_scratch'. 236 static void GenerateKeyNameCheck(MacroAssembler* masm, Register key, 237 Register map_scratch, Register hash_scratch, 238 Label* index_string, Label* not_unique) { 239 DCHECK(!AreAliased(key, map_scratch, hash_scratch)); 240 241 // Is the key a name? 242 Label unique; 243 __ JumpIfObjectType(key, map_scratch, hash_scratch, LAST_UNIQUE_NAME_TYPE, 244 not_unique, hi); 245 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE); 246 __ B(eq, &unique); 247 248 // Is the string an array index with cached numeric value? 249 __ Ldr(hash_scratch.W(), FieldMemOperand(key, Name::kHashFieldOffset)); 250 __ TestAndBranchIfAllClear(hash_scratch, Name::kContainsCachedArrayIndexMask, 251 index_string); 252 253 // Is the string internalized? We know it's a string, so a single bit test is 254 // enough. 255 __ Ldrb(hash_scratch, FieldMemOperand(map_scratch, Map::kInstanceTypeOffset)); 256 STATIC_ASSERT(kInternalizedTag == 0); 257 __ TestAndBranchIfAnySet(hash_scratch, kIsNotInternalizedMask, not_unique); 258 259 __ Bind(&unique); 260 // Fall through if the key is a unique name. 261 } 262 263 264 void LoadIC::GenerateNormal(MacroAssembler* masm, LanguageMode language_mode) { 265 Register dictionary = x0; 266 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister())); 267 DCHECK(!dictionary.is(LoadDescriptor::NameRegister())); 268 Label slow; 269 270 __ Ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(), 271 JSObject::kPropertiesOffset)); 272 GenerateDictionaryLoad(masm, &slow, dictionary, 273 LoadDescriptor::NameRegister(), x0, x3, x4); 274 __ Ret(); 275 276 // Dictionary load failed, go slow (but don't miss). 277 __ Bind(&slow); 278 GenerateRuntimeGetProperty(masm, language_mode); 279 } 280 281 282 void LoadIC::GenerateMiss(MacroAssembler* masm) { 283 // The return address is in lr. 284 Isolate* isolate = masm->isolate(); 285 ASM_LOCATION("LoadIC::GenerateMiss"); 286 287 DCHECK(!AreAliased(x4, x5, LoadWithVectorDescriptor::SlotRegister(), 288 LoadWithVectorDescriptor::VectorRegister())); 289 __ IncrementCounter(isolate->counters()->load_miss(), 1, x4, x5); 290 291 // Perform tail call to the entry. 292 __ Push(LoadWithVectorDescriptor::ReceiverRegister(), 293 LoadWithVectorDescriptor::NameRegister(), 294 LoadWithVectorDescriptor::SlotRegister(), 295 LoadWithVectorDescriptor::VectorRegister()); 296 __ TailCallRuntime(Runtime::kLoadIC_Miss); 297 } 298 299 300 void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm, 301 LanguageMode language_mode) { 302 // The return address is in lr. 303 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister()); 304 305 // Do tail-call to runtime routine. 306 __ TailCallRuntime(is_strong(language_mode) ? Runtime::kGetPropertyStrong 307 : Runtime::kGetProperty); 308 } 309 310 311 void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { 312 // The return address is in lr. 313 Isolate* isolate = masm->isolate(); 314 315 DCHECK(!AreAliased(x10, x11, LoadWithVectorDescriptor::SlotRegister(), 316 LoadWithVectorDescriptor::VectorRegister())); 317 __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, x10, x11); 318 319 __ Push(LoadWithVectorDescriptor::ReceiverRegister(), 320 LoadWithVectorDescriptor::NameRegister(), 321 LoadWithVectorDescriptor::SlotRegister(), 322 LoadWithVectorDescriptor::VectorRegister()); 323 324 // Perform tail call to the entry. 325 __ TailCallRuntime(Runtime::kKeyedLoadIC_Miss); 326 } 327 328 329 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm, 330 LanguageMode language_mode) { 331 // The return address is in lr. 332 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister()); 333 334 // Do tail-call to runtime routine. 335 __ TailCallRuntime(is_strong(language_mode) ? Runtime::kKeyedGetPropertyStrong 336 : Runtime::kKeyedGetProperty); 337 } 338 339 340 static void GenerateKeyedLoadWithSmiKey(MacroAssembler* masm, Register key, 341 Register receiver, Register scratch1, 342 Register scratch2, Register scratch3, 343 Register scratch4, Register scratch5, 344 Label* slow, 345 LanguageMode language_mode) { 346 DCHECK(!AreAliased(key, receiver, scratch1, scratch2, scratch3, scratch4, 347 scratch5)); 348 349 Isolate* isolate = masm->isolate(); 350 Label check_number_dictionary; 351 // If we can load the value, it should be returned in x0. 352 Register result = x0; 353 354 GenerateKeyedLoadReceiverCheck(masm, receiver, scratch1, scratch2, 355 Map::kHasIndexedInterceptor, slow); 356 357 // Check the receiver's map to see if it has fast elements. 358 __ CheckFastElements(scratch1, scratch2, &check_number_dictionary); 359 360 GenerateFastArrayLoad(masm, receiver, key, scratch3, scratch2, scratch1, 361 result, slow, language_mode); 362 __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, 363 scratch1, scratch2); 364 __ Ret(); 365 366 __ Bind(&check_number_dictionary); 367 __ Ldr(scratch3, FieldMemOperand(receiver, JSObject::kElementsOffset)); 368 __ Ldr(scratch2, FieldMemOperand(scratch3, JSObject::kMapOffset)); 369 370 // Check whether we have a number dictionary. 371 __ JumpIfNotRoot(scratch2, Heap::kHashTableMapRootIndex, slow); 372 373 __ LoadFromNumberDictionary(slow, scratch3, key, result, scratch1, scratch2, 374 scratch4, scratch5); 375 __ Ret(); 376 } 377 378 static void GenerateKeyedLoadWithNameKey(MacroAssembler* masm, Register key, 379 Register receiver, Register scratch1, 380 Register scratch2, Register scratch3, 381 Register scratch4, Register scratch5, 382 Label* slow) { 383 DCHECK(!AreAliased(key, receiver, scratch1, scratch2, scratch3, scratch4, 384 scratch5)); 385 386 Isolate* isolate = masm->isolate(); 387 Label probe_dictionary, property_array_property; 388 // If we can load the value, it should be returned in x0. 389 Register result = x0; 390 391 GenerateKeyedLoadReceiverCheck(masm, receiver, scratch1, scratch2, 392 Map::kHasNamedInterceptor, slow); 393 394 // If the receiver is a fast-case object, check the stub cache. Otherwise 395 // probe the dictionary. 396 __ Ldr(scratch2, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 397 __ Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset)); 398 __ JumpIfRoot(scratch3, Heap::kHashTableMapRootIndex, &probe_dictionary); 399 400 // The handlers in the stub cache expect a vector and slot. Since we won't 401 // change the IC from any downstream misses, a dummy vector can be used. 402 Register vector = LoadWithVectorDescriptor::VectorRegister(); 403 Register slot = LoadWithVectorDescriptor::SlotRegister(); 404 DCHECK(!AreAliased(vector, slot, scratch1, scratch2, scratch3, scratch4)); 405 Handle<TypeFeedbackVector> dummy_vector = 406 TypeFeedbackVector::DummyVector(masm->isolate()); 407 int slot_index = dummy_vector->GetIndex( 408 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot)); 409 __ LoadRoot(vector, Heap::kDummyVectorRootIndex); 410 __ Mov(slot, Operand(Smi::FromInt(slot_index))); 411 412 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( 413 Code::ComputeHandlerFlags(Code::LOAD_IC)); 414 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::KEYED_LOAD_IC, flags, 415 receiver, key, scratch1, 416 scratch2, scratch3, scratch4); 417 // Cache miss. 418 KeyedLoadIC::GenerateMiss(masm); 419 420 // Do a quick inline probe of the receiver's dictionary, if it exists. 421 __ Bind(&probe_dictionary); 422 __ Ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); 423 __ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); 424 GenerateGlobalInstanceTypeCheck(masm, scratch1, slow); 425 // Load the property. 426 GenerateDictionaryLoad(masm, slow, scratch2, key, result, scratch1, scratch3); 427 __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(), 1, 428 scratch1, scratch2); 429 __ Ret(); 430 } 431 432 433 void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm, 434 LanguageMode language_mode) { 435 // The return address is in lr. 436 Label slow, check_name, index_smi, index_name; 437 438 Register key = LoadDescriptor::NameRegister(); 439 Register receiver = LoadDescriptor::ReceiverRegister(); 440 DCHECK(key.is(x2)); 441 DCHECK(receiver.is(x1)); 442 443 __ JumpIfNotSmi(key, &check_name); 444 __ Bind(&index_smi); 445 // Now the key is known to be a smi. This place is also jumped to from below 446 // where a numeric string is converted to a smi. 447 GenerateKeyedLoadWithSmiKey(masm, key, receiver, x7, x3, x4, x5, x6, &slow, 448 language_mode); 449 450 // Slow case. 451 __ Bind(&slow); 452 __ IncrementCounter(masm->isolate()->counters()->keyed_load_generic_slow(), 1, 453 x4, x3); 454 GenerateRuntimeGetProperty(masm, language_mode); 455 456 __ Bind(&check_name); 457 GenerateKeyNameCheck(masm, key, x0, x3, &index_name, &slow); 458 459 GenerateKeyedLoadWithNameKey(masm, key, receiver, x4, x5, x6, x7, x3, &slow); 460 461 __ Bind(&index_name); 462 __ IndexFromHash(x3, key); 463 // Now jump to the place where smi keys are handled. 464 __ B(&index_smi); 465 } 466 467 468 static void StoreIC_PushArgs(MacroAssembler* masm) { 469 __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(), 470 StoreDescriptor::ValueRegister(), 471 VectorStoreICDescriptor::SlotRegister(), 472 VectorStoreICDescriptor::VectorRegister()); 473 } 474 475 476 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { 477 ASM_LOCATION("KeyedStoreIC::GenerateMiss"); 478 StoreIC_PushArgs(masm); 479 __ TailCallRuntime(Runtime::kKeyedStoreIC_Miss); 480 } 481 482 483 static void KeyedStoreGenerateMegamorphicHelper( 484 MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow, 485 KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length, 486 Register value, Register key, Register receiver, Register receiver_map, 487 Register elements_map, Register elements) { 488 DCHECK(!AreAliased(value, key, receiver, receiver_map, elements_map, elements, 489 x10, x11)); 490 491 Label transition_smi_elements; 492 Label transition_double_elements; 493 Label fast_double_without_map_check; 494 Label non_double_value; 495 Label finish_store; 496 497 __ Bind(fast_object); 498 if (check_map == kCheckMap) { 499 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); 500 __ Cmp(elements_map, 501 Operand(masm->isolate()->factory()->fixed_array_map())); 502 __ B(ne, fast_double); 503 } 504 505 // HOLECHECK: guards "A[i] = V" 506 // We have to go to the runtime if the current value is the hole because there 507 // may be a callback on the element. 508 Label holecheck_passed; 509 __ Add(x10, elements, FixedArray::kHeaderSize - kHeapObjectTag); 510 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); 511 __ Ldr(x11, MemOperand(x10)); 512 __ JumpIfNotRoot(x11, Heap::kTheHoleValueRootIndex, &holecheck_passed); 513 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); 514 __ bind(&holecheck_passed); 515 516 // Smi stores don't require further checks. 517 __ JumpIfSmi(value, &finish_store); 518 519 // Escape to elements kind transition case. 520 __ CheckFastObjectElements(receiver_map, x10, &transition_smi_elements); 521 522 __ Bind(&finish_store); 523 if (increment_length == kIncrementLength) { 524 // Add 1 to receiver->length. 525 __ Add(x10, key, Smi::FromInt(1)); 526 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); 527 } 528 529 Register address = x11; 530 __ Add(address, elements, FixedArray::kHeaderSize - kHeapObjectTag); 531 __ Add(address, address, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); 532 __ Str(value, MemOperand(address)); 533 534 Label dont_record_write; 535 __ JumpIfSmi(value, &dont_record_write); 536 537 // Update write barrier for the elements array address. 538 __ Mov(x10, value); // Preserve the value which is returned. 539 __ RecordWrite(elements, address, x10, kLRHasNotBeenSaved, kDontSaveFPRegs, 540 EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); 541 542 __ Bind(&dont_record_write); 543 __ Ret(); 544 545 546 __ Bind(fast_double); 547 if (check_map == kCheckMap) { 548 // Check for fast double array case. If this fails, call through to the 549 // runtime. 550 __ JumpIfNotRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex, slow); 551 } 552 553 // HOLECHECK: guards "A[i] double hole?" 554 // We have to see if the double version of the hole is present. If so go to 555 // the runtime. 556 __ Add(x10, elements, FixedDoubleArray::kHeaderSize - kHeapObjectTag); 557 __ Add(x10, x10, Operand::UntagSmiAndScale(key, kPointerSizeLog2)); 558 __ Ldr(x11, MemOperand(x10)); 559 __ CompareAndBranch(x11, kHoleNanInt64, ne, &fast_double_without_map_check); 560 __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, x10, slow); 561 562 __ Bind(&fast_double_without_map_check); 563 __ StoreNumberToDoubleElements(value, key, elements, x10, d0, 564 &transition_double_elements); 565 if (increment_length == kIncrementLength) { 566 // Add 1 to receiver->length. 567 __ Add(x10, key, Smi::FromInt(1)); 568 __ Str(x10, FieldMemOperand(receiver, JSArray::kLengthOffset)); 569 } 570 __ Ret(); 571 572 573 __ Bind(&transition_smi_elements); 574 // Transition the array appropriately depending on the value type. 575 __ Ldr(x10, FieldMemOperand(value, HeapObject::kMapOffset)); 576 __ JumpIfNotRoot(x10, Heap::kHeapNumberMapRootIndex, &non_double_value); 577 578 // Value is a double. Transition FAST_SMI_ELEMENTS -> 579 // FAST_DOUBLE_ELEMENTS and complete the store. 580 __ LoadTransitionedArrayMapConditional( 581 FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, x10, x11, slow); 582 AllocationSiteMode mode = 583 AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS); 584 ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value, 585 receiver_map, mode, slow); 586 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 587 __ B(&fast_double_without_map_check); 588 589 __ Bind(&non_double_value); 590 // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS. 591 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS, 592 receiver_map, x10, x11, slow); 593 594 mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS); 595 ElementsTransitionGenerator::GenerateMapChangeElementsTransition( 596 masm, receiver, key, value, receiver_map, mode, slow); 597 598 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 599 __ B(&finish_store); 600 601 __ Bind(&transition_double_elements); 602 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a 603 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and 604 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS 605 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS, 606 receiver_map, x10, x11, slow); 607 mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS); 608 ElementsTransitionGenerator::GenerateDoubleToObject( 609 masm, receiver, key, value, receiver_map, mode, slow); 610 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 611 __ B(&finish_store); 612 } 613 614 615 void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm, 616 LanguageMode language_mode) { 617 ASM_LOCATION("KeyedStoreIC::GenerateMegamorphic"); 618 Label slow; 619 Label array; 620 Label fast_object; 621 Label extra; 622 Label fast_object_grow; 623 Label fast_double_grow; 624 Label fast_double; 625 Label maybe_name_key; 626 Label miss; 627 628 Register value = StoreDescriptor::ValueRegister(); 629 Register key = StoreDescriptor::NameRegister(); 630 Register receiver = StoreDescriptor::ReceiverRegister(); 631 DCHECK(receiver.is(x1)); 632 DCHECK(key.is(x2)); 633 DCHECK(value.is(x0)); 634 635 Register receiver_map = x3; 636 Register elements = x4; 637 Register elements_map = x5; 638 639 __ JumpIfNotSmi(key, &maybe_name_key); 640 __ JumpIfSmi(receiver, &slow); 641 __ Ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset)); 642 643 // Check that the receiver does not require access checks and is not observed. 644 // The generic stub does not perform map checks or handle observed objects. 645 __ Ldrb(x10, FieldMemOperand(receiver_map, Map::kBitFieldOffset)); 646 __ TestAndBranchIfAnySet( 647 x10, (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kIsObserved), &slow); 648 649 // Check if the object is a JS array or not. 650 Register instance_type = x10; 651 __ CompareInstanceType(receiver_map, instance_type, JS_ARRAY_TYPE); 652 __ B(eq, &array); 653 // Check that the object is some kind of JS object EXCEPT JS Value type. In 654 // the case that the object is a value-wrapper object, we enter the runtime 655 // system to make sure that indexing into string objects works as intended. 656 STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE); 657 __ Cmp(instance_type, JS_OBJECT_TYPE); 658 __ B(lo, &slow); 659 660 // Object case: Check key against length in the elements array. 661 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 662 // Check array bounds. Both the key and the length of FixedArray are smis. 663 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); 664 __ Cmp(x10, Operand::UntagSmi(key)); 665 __ B(hi, &fast_object); 666 667 668 __ Bind(&slow); 669 // Slow case, handle jump to runtime. 670 // Live values: 671 // x0: value 672 // x1: key 673 // x2: receiver 674 PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode); 675 // Never returns to here. 676 677 __ bind(&maybe_name_key); 678 __ Ldr(x10, FieldMemOperand(key, HeapObject::kMapOffset)); 679 __ Ldrb(x10, FieldMemOperand(x10, Map::kInstanceTypeOffset)); 680 __ JumpIfNotUniqueNameInstanceType(x10, &slow); 681 682 // The handlers in the stub cache expect a vector and slot. Since we won't 683 // change the IC from any downstream misses, a dummy vector can be used. 684 Register vector = VectorStoreICDescriptor::VectorRegister(); 685 Register slot = VectorStoreICDescriptor::SlotRegister(); 686 DCHECK(!AreAliased(vector, slot, x5, x6, x7, x8)); 687 Handle<TypeFeedbackVector> dummy_vector = 688 TypeFeedbackVector::DummyVector(masm->isolate()); 689 int slot_index = dummy_vector->GetIndex( 690 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot)); 691 __ LoadRoot(vector, Heap::kDummyVectorRootIndex); 692 __ Mov(slot, Operand(Smi::FromInt(slot_index))); 693 694 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( 695 Code::ComputeHandlerFlags(Code::STORE_IC)); 696 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags, 697 receiver, key, x5, x6, x7, x8); 698 // Cache miss. 699 __ B(&miss); 700 701 __ Bind(&extra); 702 // Extra capacity case: Check if there is extra capacity to 703 // perform the store and update the length. Used for adding one 704 // element to the array by writing to array[array.length]. 705 706 // Check for room in the elements backing store. 707 // Both the key and the length of FixedArray are smis. 708 __ Ldrsw(x10, UntagSmiFieldMemOperand(elements, FixedArray::kLengthOffset)); 709 __ Cmp(x10, Operand::UntagSmi(key)); 710 __ B(ls, &slow); 711 712 __ Ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset)); 713 __ Cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map())); 714 __ B(eq, &fast_object_grow); 715 __ Cmp(elements_map, 716 Operand(masm->isolate()->factory()->fixed_double_array_map())); 717 __ B(eq, &fast_double_grow); 718 __ B(&slow); 719 720 721 __ Bind(&array); 722 // Array case: Get the length and the elements array from the JS 723 // array. Check that the array is in fast mode (and writable); if it 724 // is the length is always a smi. 725 726 __ Ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); 727 728 // Check the key against the length in the array. 729 __ Ldrsw(x10, UntagSmiFieldMemOperand(receiver, JSArray::kLengthOffset)); 730 __ Cmp(x10, Operand::UntagSmi(key)); 731 __ B(eq, &extra); // We can handle the case where we are appending 1 element. 732 __ B(lo, &slow); 733 734 KeyedStoreGenerateMegamorphicHelper( 735 masm, &fast_object, &fast_double, &slow, kCheckMap, kDontIncrementLength, 736 value, key, receiver, receiver_map, elements_map, elements); 737 KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow, 738 &fast_double_grow, &slow, kDontCheckMap, 739 kIncrementLength, value, key, receiver, 740 receiver_map, elements_map, elements); 741 742 __ bind(&miss); 743 GenerateMiss(masm); 744 } 745 746 747 void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { 748 Register receiver = StoreDescriptor::ReceiverRegister(); 749 Register name = StoreDescriptor::NameRegister(); 750 DCHECK(!AreAliased(receiver, name, StoreDescriptor::ValueRegister(), x3, x4, 751 x5, x6)); 752 753 // Probe the stub cache. 754 Code::Flags flags = Code::RemoveTypeAndHolderFromFlags( 755 Code::ComputeHandlerFlags(Code::STORE_IC)); 756 masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags, 757 receiver, name, x3, x4, x5, x6); 758 759 // Cache miss: Jump to runtime. 760 GenerateMiss(masm); 761 } 762 763 764 void StoreIC::GenerateMiss(MacroAssembler* masm) { 765 StoreIC_PushArgs(masm); 766 767 // Tail call to the entry. 768 __ TailCallRuntime(Runtime::kStoreIC_Miss); 769 } 770 771 772 void StoreIC::GenerateNormal(MacroAssembler* masm) { 773 Label miss; 774 Register value = StoreDescriptor::ValueRegister(); 775 Register receiver = StoreDescriptor::ReceiverRegister(); 776 Register name = StoreDescriptor::NameRegister(); 777 Register dictionary = x5; 778 DCHECK(!AreAliased(value, receiver, name, 779 VectorStoreICDescriptor::SlotRegister(), 780 VectorStoreICDescriptor::VectorRegister(), x5, x6, x7)); 781 782 __ Ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 783 784 GenerateDictionaryStore(masm, &miss, dictionary, name, value, x6, x7); 785 Counters* counters = masm->isolate()->counters(); 786 __ IncrementCounter(counters->store_normal_hit(), 1, x6, x7); 787 __ Ret(); 788 789 // Cache miss: Jump to runtime. 790 __ Bind(&miss); 791 __ IncrementCounter(counters->store_normal_miss(), 1, x6, x7); 792 GenerateMiss(masm); 793 } 794 795 796 Condition CompareIC::ComputeCondition(Token::Value op) { 797 switch (op) { 798 case Token::EQ_STRICT: 799 case Token::EQ: 800 return eq; 801 case Token::LT: 802 return lt; 803 case Token::GT: 804 return gt; 805 case Token::LTE: 806 return le; 807 case Token::GTE: 808 return ge; 809 default: 810 UNREACHABLE(); 811 return al; 812 } 813 } 814 815 816 bool CompareIC::HasInlinedSmiCode(Address address) { 817 // The address of the instruction following the call. 818 Address info_address = Assembler::return_address_from_call_start(address); 819 820 InstructionSequence* patch_info = InstructionSequence::At(info_address); 821 return patch_info->IsInlineData(); 822 } 823 824 825 // Activate a SMI fast-path by patching the instructions generated by 826 // JumpPatchSite::EmitJumpIf(Not)Smi(), using the information encoded by 827 // JumpPatchSite::EmitPatchInfo(). 828 void PatchInlinedSmiCode(Isolate* isolate, Address address, 829 InlinedSmiCheck check) { 830 // The patch information is encoded in the instruction stream using 831 // instructions which have no side effects, so we can safely execute them. 832 // The patch information is encoded directly after the call to the helper 833 // function which is requesting this patch operation. 834 Address info_address = Assembler::return_address_from_call_start(address); 835 InlineSmiCheckInfo info(info_address); 836 837 // Check and decode the patch information instruction. 838 if (!info.HasSmiCheck()) { 839 return; 840 } 841 842 if (FLAG_trace_ic) { 843 PrintF("[ Patching ic at %p, marker=%p, SMI check=%p\n", address, 844 info_address, reinterpret_cast<void*>(info.SmiCheck())); 845 } 846 847 // Patch and activate code generated by JumpPatchSite::EmitJumpIfNotSmi() 848 // and JumpPatchSite::EmitJumpIfSmi(). 849 // Changing 850 // tb(n)z xzr, #0, <target> 851 // to 852 // tb(!n)z test_reg, #0, <target> 853 Instruction* to_patch = info.SmiCheck(); 854 PatchingAssembler patcher(isolate, to_patch, 1); 855 DCHECK(to_patch->IsTestBranch()); 856 DCHECK(to_patch->ImmTestBranchBit5() == 0); 857 DCHECK(to_patch->ImmTestBranchBit40() == 0); 858 859 STATIC_ASSERT(kSmiTag == 0); 860 STATIC_ASSERT(kSmiTagMask == 1); 861 862 int branch_imm = to_patch->ImmTestBranch(); 863 Register smi_reg; 864 if (check == ENABLE_INLINED_SMI_CHECK) { 865 DCHECK(to_patch->Rt() == xzr.code()); 866 smi_reg = info.SmiRegister(); 867 } else { 868 DCHECK(check == DISABLE_INLINED_SMI_CHECK); 869 DCHECK(to_patch->Rt() != xzr.code()); 870 smi_reg = xzr; 871 } 872 873 if (to_patch->Mask(TestBranchMask) == TBZ) { 874 // This is JumpIfNotSmi(smi_reg, branch_imm). 875 patcher.tbnz(smi_reg, 0, branch_imm); 876 } else { 877 DCHECK(to_patch->Mask(TestBranchMask) == TBNZ); 878 // This is JumpIfSmi(smi_reg, branch_imm). 879 patcher.tbz(smi_reg, 0, branch_imm); 880 } 881 } 882 } // namespace internal 883 } // namespace v8 884 885 #endif // V8_TARGET_ARCH_ARM64 886