1// Copyright 2010 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#include "codegen-inl.h" 31#include "ic-inl.h" 32#include "runtime.h" 33#include "stub-cache.h" 34#include "utils.h" 35 36namespace v8 { 37namespace internal { 38 39// ---------------------------------------------------------------------------- 40// Static IC stub generators. 41// 42 43#define __ ACCESS_MASM(masm) 44 45 46// Helper function used to load a property from a dictionary backing storage. 47// This function may return false negatives, so miss_label 48// must always call a backup property load that is complete. 49// This function is safe to call if the receiver has fast properties, 50// or if name is not a symbol, and will jump to the miss_label in that case. 51static void GenerateDictionaryLoad(MacroAssembler* masm, 52 Label* miss_label, 53 Register r0, 54 Register r1, 55 Register r2, 56 Register name, 57 DictionaryCheck check_dictionary) { 58 // Register use: 59 // 60 // r0 - used to hold the property dictionary. 61 // 62 // r1 - initially the receiver 63 // - used for the index into the property dictionary 64 // - holds the result on exit. 65 // 66 // r2 - used to hold the capacity of the property dictionary. 67 // 68 // name - holds the name of the property and is unchanged. 69 70 Label done; 71 72 // Check for the absence of an interceptor. 73 // Load the map into r0. 74 __ movq(r0, FieldOperand(r1, JSObject::kMapOffset)); 75 // Test the has_named_interceptor bit in the map. 76 __ testl(FieldOperand(r0, Map::kInstanceAttributesOffset), 77 Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8)))); 78 79 // Jump to miss if the interceptor bit is set. 80 __ j(not_zero, miss_label); 81 82 // Bail out if we have a JS global proxy object. 83 __ movzxbq(r0, FieldOperand(r0, Map::kInstanceTypeOffset)); 84 __ cmpb(r0, Immediate(JS_GLOBAL_PROXY_TYPE)); 85 __ j(equal, miss_label); 86 87 // Possible work-around for http://crbug.com/16276. 88 __ cmpb(r0, Immediate(JS_GLOBAL_OBJECT_TYPE)); 89 __ j(equal, miss_label); 90 __ cmpb(r0, Immediate(JS_BUILTINS_OBJECT_TYPE)); 91 __ j(equal, miss_label); 92 93 // Load properties array. 94 __ movq(r0, FieldOperand(r1, JSObject::kPropertiesOffset)); 95 96 if (check_dictionary == CHECK_DICTIONARY) { 97 // Check that the properties array is a dictionary. 98 __ Cmp(FieldOperand(r0, HeapObject::kMapOffset), Factory::hash_table_map()); 99 __ j(not_equal, miss_label); 100 } 101 102 // Compute the capacity mask. 103 const int kCapacityOffset = 104 StringDictionary::kHeaderSize + 105 StringDictionary::kCapacityIndex * kPointerSize; 106 __ movq(r2, FieldOperand(r0, kCapacityOffset)); 107 __ SmiToInteger32(r2, r2); 108 __ decl(r2); 109 110 // Generate an unrolled loop that performs a few probes before 111 // giving up. Measurements done on Gmail indicate that 2 probes 112 // cover ~93% of loads from dictionaries. 113 static const int kProbes = 4; 114 const int kElementsStartOffset = 115 StringDictionary::kHeaderSize + 116 StringDictionary::kElementsStartIndex * kPointerSize; 117 for (int i = 0; i < kProbes; i++) { 118 // Compute the masked index: (hash + i + i * i) & mask. 119 __ movl(r1, FieldOperand(name, String::kHashFieldOffset)); 120 __ shrl(r1, Immediate(String::kHashShift)); 121 if (i > 0) { 122 __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i))); 123 } 124 __ and_(r1, r2); 125 126 // Scale the index by multiplying by the entry size. 127 ASSERT(StringDictionary::kEntrySize == 3); 128 __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3 129 130 // Check if the key is identical to the name. 131 __ cmpq(name, Operand(r0, r1, times_pointer_size, 132 kElementsStartOffset - kHeapObjectTag)); 133 if (i != kProbes - 1) { 134 __ j(equal, &done); 135 } else { 136 __ j(not_equal, miss_label); 137 } 138 } 139 140 // Check that the value is a normal property. 141 __ bind(&done); 142 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; 143 __ Test(Operand(r0, r1, times_pointer_size, kDetailsOffset - kHeapObjectTag), 144 Smi::FromInt(PropertyDetails::TypeField::mask())); 145 __ j(not_zero, miss_label); 146 147 // Get the value at the masked, scaled index. 148 const int kValueOffset = kElementsStartOffset + kPointerSize; 149 __ movq(r1, 150 Operand(r0, r1, times_pointer_size, kValueOffset - kHeapObjectTag)); 151} 152 153 154// One byte opcode for test eax,0xXXXXXXXX. 155static const byte kTestEaxByte = 0xA9; 156 157 158static bool PatchInlinedMapCheck(Address address, Object* map) { 159 // Arguments are address of start of call sequence that called 160 // the IC, 161 Address test_instruction_address = 162 address + Assembler::kCallTargetAddressOffset; 163 // The keyed load has a fast inlined case if the IC call instruction 164 // is immediately followed by a test instruction. 165 if (*test_instruction_address != kTestEaxByte) return false; 166 167 // Fetch the offset from the test instruction to the map compare 168 // instructions (starting with the 64-bit immediate mov of the map 169 // address). This offset is stored in the last 4 bytes of the 5 170 // byte test instruction. 171 Address delta_address = test_instruction_address + 1; 172 int delta = *reinterpret_cast<int*>(delta_address); 173 // Compute the map address. The map address is in the last 8 bytes 174 // of the 10-byte immediate mov instruction (incl. REX prefix), so we add 2 175 // to the offset to get the map address. 176 Address map_address = test_instruction_address + delta + 2; 177 // Patch the map check. 178 *(reinterpret_cast<Object**>(map_address)) = map; 179 return true; 180} 181 182 183bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { 184 return PatchInlinedMapCheck(address, map); 185} 186 187 188bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) { 189 return PatchInlinedMapCheck(address, map); 190} 191 192 193void KeyedLoadIC::ClearInlinedVersion(Address address) { 194 // Insert null as the map to check for to make sure the map check fails 195 // sending control flow to the IC instead of the inlined version. 196 PatchInlinedLoad(address, Heap::null_value()); 197} 198 199 200void KeyedStoreIC::ClearInlinedVersion(Address address) { 201 // Insert null as the elements map to check for. This will make 202 // sure that the elements fast-case map check fails so that control 203 // flows to the IC instead of the inlined version. 204 PatchInlinedStore(address, Heap::null_value()); 205} 206 207 208void KeyedStoreIC::RestoreInlinedVersion(Address address) { 209 // Restore the fast-case elements map check so that the inlined 210 // version can be used again. 211 PatchInlinedStore(address, Heap::fixed_array_map()); 212} 213 214 215void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { 216 // ----------- S t a t e ------------- 217 // -- rsp[0] : return address 218 // -- rsp[8] : name 219 // -- rsp[16] : receiver 220 // ----------------------------------- 221 222 __ pop(rbx); 223 __ push(Operand(rsp, 1 * kPointerSize)); // receiver 224 __ push(Operand(rsp, 1 * kPointerSize)); // name 225 __ push(rbx); // return address 226 227 // Perform tail call to the entry. 228 __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1); 229} 230 231 232void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { 233 // ----------- S t a t e ------------- 234 // -- rsp[0] : return address 235 // -- rsp[8] : name 236 // -- rsp[16] : receiver 237 // ----------------------------------- 238 239 __ pop(rbx); 240 __ push(Operand(rsp, 1 * kPointerSize)); // receiver 241 __ push(Operand(rsp, 1 * kPointerSize)); // name 242 __ push(rbx); // return address 243 244 // Perform tail call to the entry. 245 __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1); 246} 247 248 249void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { 250 // ----------- S t a t e ------------- 251 // -- rsp[0] : return address 252 // -- rsp[8] : name 253 // -- rsp[16] : receiver 254 // ----------------------------------- 255 Label slow, check_string, index_int, index_string; 256 Label check_pixel_array, probe_dictionary; 257 258 // Load name and receiver. 259 __ movq(rax, Operand(rsp, kPointerSize)); 260 __ movq(rcx, Operand(rsp, 2 * kPointerSize)); 261 262 // Check that the object isn't a smi. 263 __ JumpIfSmi(rcx, &slow); 264 265 // Check that the object is some kind of JS object EXCEPT JS Value type. 266 // In the case that the object is a value-wrapper object, 267 // we enter the runtime system to make sure that indexing 268 // into string objects work as intended. 269 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); 270 __ CmpObjectType(rcx, JS_OBJECT_TYPE, rdx); 271 __ j(below, &slow); 272 273 // Check bit field. 274 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 275 Immediate(kSlowCaseBitFieldMask)); 276 __ j(not_zero, &slow); 277 278 // Check that the key is a smi. 279 __ JumpIfNotSmi(rax, &check_string); 280 __ SmiToInteger32(rax, rax); 281 // Get the elements array of the object. 282 __ bind(&index_int); 283 __ movq(rcx, FieldOperand(rcx, JSObject::kElementsOffset)); 284 // Check that the object is in fast mode (not dictionary). 285 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 286 Heap::kFixedArrayMapRootIndex); 287 __ j(not_equal, &check_pixel_array); 288 // Check that the key (index) is within bounds. 289 __ cmpl(rax, FieldOperand(rcx, FixedArray::kLengthOffset)); 290 __ j(above_equal, &slow); // Unsigned comparison rejects negative indices. 291 // Fast case: Do the load. 292 __ movq(rax, Operand(rcx, rax, times_pointer_size, 293 FixedArray::kHeaderSize - kHeapObjectTag)); 294 __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); 295 // In case the loaded value is the_hole we have to consult GetProperty 296 // to ensure the prototype chain is searched. 297 __ j(equal, &slow); 298 __ IncrementCounter(&Counters::keyed_load_generic_smi, 1); 299 __ ret(0); 300 301 // Check whether the elements is a pixel array. 302 // rax: untagged index 303 // rcx: elements array 304 __ bind(&check_pixel_array); 305 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 306 Heap::kPixelArrayMapRootIndex); 307 __ j(not_equal, &slow); 308 __ cmpl(rax, FieldOperand(rcx, PixelArray::kLengthOffset)); 309 __ j(above_equal, &slow); 310 __ movq(rcx, FieldOperand(rcx, PixelArray::kExternalPointerOffset)); 311 __ movzxbq(rax, Operand(rcx, rax, times_1, 0)); 312 __ Integer32ToSmi(rax, rax); 313 __ ret(0); 314 315 // Slow case: Load name and receiver from stack and jump to runtime. 316 __ bind(&slow); 317 __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); 318 GenerateRuntimeGetProperty(masm); 319 __ bind(&check_string); 320 // The key is not a smi. 321 // Is it a string? 322 __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdx); 323 __ j(above_equal, &slow); 324 // Is the string an array index, with cached numeric value? 325 __ movl(rbx, FieldOperand(rax, String::kHashFieldOffset)); 326 __ testl(rbx, Immediate(String::kIsArrayIndexMask)); 327 328 // Is the string a symbol? 329 __ j(not_zero, &index_string); // The value in rbx is used at jump target. 330 ASSERT(kSymbolTag != 0); 331 __ testb(FieldOperand(rdx, Map::kInstanceTypeOffset), 332 Immediate(kIsSymbolMask)); 333 __ j(zero, &slow); 334 335 // If the receiver is a fast-case object, check the keyed lookup 336 // cache. Otherwise probe the dictionary leaving result in rcx. 337 __ movq(rbx, FieldOperand(rcx, JSObject::kPropertiesOffset)); 338 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::hash_table_map()); 339 __ j(equal, &probe_dictionary); 340 341 // Load the map of the receiver, compute the keyed lookup cache hash 342 // based on 32 bits of the map pointer and the string hash. 343 __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); 344 __ movl(rdx, rbx); 345 __ shr(rdx, Immediate(KeyedLookupCache::kMapHashShift)); 346 __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); 347 __ shr(rax, Immediate(String::kHashShift)); 348 __ xor_(rdx, rax); 349 __ and_(rdx, Immediate(KeyedLookupCache::kCapacityMask)); 350 351 // Load the key (consisting of map and symbol) from the cache and 352 // check for match. 353 ExternalReference cache_keys 354 = ExternalReference::keyed_lookup_cache_keys(); 355 __ movq(rdi, rdx); 356 __ shl(rdi, Immediate(kPointerSizeLog2 + 1)); 357 __ movq(kScratchRegister, cache_keys); 358 __ cmpq(rbx, Operand(kScratchRegister, rdi, times_1, 0)); 359 __ j(not_equal, &slow); 360 __ movq(rdi, Operand(kScratchRegister, rdi, times_1, kPointerSize)); 361 __ cmpq(Operand(rsp, kPointerSize), rdi); 362 __ j(not_equal, &slow); 363 364 // Get field offset which is a 32-bit integer and check that it is 365 // an in-object property. 366 ExternalReference cache_field_offsets 367 = ExternalReference::keyed_lookup_cache_field_offsets(); 368 __ movq(kScratchRegister, cache_field_offsets); 369 __ movl(rax, Operand(kScratchRegister, rdx, times_4, 0)); 370 __ movzxbq(rdx, FieldOperand(rbx, Map::kInObjectPropertiesOffset)); 371 __ cmpq(rax, rdx); 372 __ j(above_equal, &slow); 373 374 // Load in-object property. 375 __ subq(rax, rdx); 376 __ movzxbq(rdx, FieldOperand(rbx, Map::kInstanceSizeOffset)); 377 __ addq(rax, rdx); 378 __ movq(rax, FieldOperand(rcx, rax, times_pointer_size, 0)); 379 __ ret(0); 380 381 // Do a quick inline probe of the receiver's dictionary, if it 382 // exists. 383 __ bind(&probe_dictionary); 384 GenerateDictionaryLoad(masm, 385 &slow, 386 rbx, 387 rcx, 388 rdx, 389 rax, 390 DICTIONARY_CHECK_DONE); 391 __ movq(rax, rcx); 392 __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); 393 __ ret(0); 394 // If the hash field contains an array index pick it out. The assert checks 395 // that the constants for the maximum number of digits for an array index 396 // cached in the hash field and the number of bits reserved for it does not 397 // conflict. 398 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < 399 (1 << String::kArrayIndexValueBits)); 400 __ bind(&index_string); 401 __ movl(rax, rbx); 402 __ and_(rax, Immediate(String::kArrayIndexHashMask)); 403 __ shrl(rax, Immediate(String::kHashShift)); 404 __ jmp(&index_int); 405} 406 407 408void KeyedLoadIC::GenerateString(MacroAssembler* masm) { 409 // ----------- S t a t e ------------- 410 // -- rsp[0] : return address 411 // -- rsp[8] : name 412 // -- rsp[16] : receiver 413 // ----------------------------------- 414 415 GenerateGeneric(masm); 416} 417 418 419void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, 420 ExternalArrayType array_type) { 421 // ----------- S t a t e ------------- 422 // -- rsp[0] : return address 423 // -- rsp[8] : name 424 // -- rsp[16] : receiver 425 // ----------------------------------- 426 Label slow, failed_allocation; 427 428 // Load name and receiver. 429 __ movq(rax, Operand(rsp, kPointerSize)); 430 __ movq(rcx, Operand(rsp, 2 * kPointerSize)); 431 432 // Check that the object isn't a smi. 433 __ JumpIfSmi(rcx, &slow); 434 435 // Check that the key is a smi. 436 __ JumpIfNotSmi(rax, &slow); 437 438 // Check that the object is a JS object. 439 __ CmpObjectType(rcx, JS_OBJECT_TYPE, rdx); 440 __ j(not_equal, &slow); 441 // Check that the receiver does not require access checks. We need 442 // to check this explicitly since this generic stub does not perform 443 // map checks. The map is already in rdx. 444 __ testb(FieldOperand(rdx, Map::kBitFieldOffset), 445 Immediate(1 << Map::kIsAccessCheckNeeded)); 446 __ j(not_zero, &slow); 447 448 // Check that the elements array is the appropriate type of 449 // ExternalArray. 450 // rax: index (as a smi) 451 // rcx: JSObject 452 __ movq(rcx, FieldOperand(rcx, JSObject::kElementsOffset)); 453 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 454 Heap::RootIndexForExternalArrayType(array_type)); 455 __ j(not_equal, &slow); 456 457 // Check that the index is in range. 458 __ SmiToInteger32(rax, rax); 459 __ cmpl(rax, FieldOperand(rcx, ExternalArray::kLengthOffset)); 460 // Unsigned comparison catches both negative and too-large values. 461 __ j(above_equal, &slow); 462 463 // rax: untagged index 464 // rcx: elements array 465 __ movq(rcx, FieldOperand(rcx, ExternalArray::kExternalPointerOffset)); 466 // rcx: base pointer of external storage 467 switch (array_type) { 468 case kExternalByteArray: 469 __ movsxbq(rax, Operand(rcx, rax, times_1, 0)); 470 break; 471 case kExternalUnsignedByteArray: 472 __ movzxbq(rax, Operand(rcx, rax, times_1, 0)); 473 break; 474 case kExternalShortArray: 475 __ movsxwq(rax, Operand(rcx, rax, times_2, 0)); 476 break; 477 case kExternalUnsignedShortArray: 478 __ movzxwq(rax, Operand(rcx, rax, times_2, 0)); 479 break; 480 case kExternalIntArray: 481 __ movsxlq(rax, Operand(rcx, rax, times_4, 0)); 482 break; 483 case kExternalUnsignedIntArray: 484 __ movl(rax, Operand(rcx, rax, times_4, 0)); 485 break; 486 case kExternalFloatArray: 487 __ fld_s(Operand(rcx, rax, times_4, 0)); 488 break; 489 default: 490 UNREACHABLE(); 491 break; 492 } 493 494 // For integer array types: 495 // rax: value 496 // For floating-point array type: 497 // FP(0): value 498 499 if (array_type == kExternalIntArray || 500 array_type == kExternalUnsignedIntArray) { 501 // For the Int and UnsignedInt array types, we need to see whether 502 // the value can be represented in a Smi. If not, we need to convert 503 // it to a HeapNumber. 504 Label box_int; 505 if (array_type == kExternalIntArray) { 506 __ JumpIfNotValidSmiValue(rax, &box_int); 507 } else { 508 ASSERT_EQ(array_type, kExternalUnsignedIntArray); 509 __ JumpIfUIntNotValidSmiValue(rax, &box_int); 510 } 511 512 __ Integer32ToSmi(rax, rax); 513 __ ret(0); 514 515 __ bind(&box_int); 516 517 // Allocate a HeapNumber for the int and perform int-to-double 518 // conversion. 519 __ push(rax); 520 if (array_type == kExternalIntArray) { 521 __ fild_s(Operand(rsp, 0)); 522 } else { 523 ASSERT(array_type == kExternalUnsignedIntArray); 524 // Need to zero-extend the value. 525 __ fild_d(Operand(rsp, 0)); 526 } 527 __ pop(rax); 528 // FP(0): value 529 __ AllocateHeapNumber(rax, rbx, &failed_allocation); 530 // Set the value. 531 __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset)); 532 __ ret(0); 533 } else if (array_type == kExternalFloatArray) { 534 // For the floating-point array type, we need to always allocate a 535 // HeapNumber. 536 __ AllocateHeapNumber(rax, rbx, &failed_allocation); 537 // Set the value. 538 __ fstp_d(FieldOperand(rax, HeapNumber::kValueOffset)); 539 __ ret(0); 540 } else { 541 __ Integer32ToSmi(rax, rax); 542 __ ret(0); 543 } 544 545 // If we fail allocation of the HeapNumber, we still have a value on 546 // top of the FPU stack. Remove it. 547 __ bind(&failed_allocation); 548 __ ffree(); 549 __ fincstp(); 550 // Fall through to slow case. 551 552 // Slow case: Load name and receiver from stack and jump to runtime. 553 __ bind(&slow); 554 __ IncrementCounter(&Counters::keyed_load_external_array_slow, 1); 555 GenerateRuntimeGetProperty(masm); 556} 557 558 559void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { 560 // ----------- S t a t e ------------- 561 // -- rsp[0] : return address 562 // -- rsp[8] : key 563 // -- rsp[16] : receiver 564 // ----------------------------------- 565 Label slow; 566 567 // Load key and receiver. 568 __ movq(rax, Operand(rsp, kPointerSize)); 569 __ movq(rcx, Operand(rsp, 2 * kPointerSize)); 570 571 // Check that the receiver isn't a smi. 572 __ JumpIfSmi(rcx, &slow); 573 574 // Check that the key is a smi. 575 __ JumpIfNotSmi(rax, &slow); 576 577 // Get the map of the receiver. 578 __ movq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); 579 580 // Check that it has indexed interceptor and access checks 581 // are not enabled for this object. 582 __ movb(rdx, FieldOperand(rdx, Map::kBitFieldOffset)); 583 __ andb(rdx, Immediate(kSlowCaseBitFieldMask)); 584 __ cmpb(rdx, Immediate(1 << Map::kHasIndexedInterceptor)); 585 __ j(not_zero, &slow); 586 587 // Everything is fine, call runtime. 588 __ pop(rdx); 589 __ push(rcx); // receiver 590 __ push(rax); // key 591 __ push(rdx); // return address 592 593 // Perform tail call to the entry. 594 __ TailCallRuntime(ExternalReference( 595 IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1); 596 597 __ bind(&slow); 598 GenerateMiss(masm); 599} 600 601 602void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { 603 // ----------- S t a t e ------------- 604 // -- rax : value 605 // -- rsp[0] : return address 606 // -- rsp[8] : key 607 // -- rsp[16] : receiver 608 // ----------------------------------- 609 610 __ pop(rcx); 611 __ push(Operand(rsp, 1 * kPointerSize)); // receiver 612 __ push(Operand(rsp, 1 * kPointerSize)); // key 613 __ push(rax); // value 614 __ push(rcx); // return address 615 616 // Do tail-call to runtime routine. 617 __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1); 618} 619 620 621void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { 622 // ----------- S t a t e ------------- 623 // -- rax : value 624 // -- rsp[0] : return address 625 // -- rsp[8] : key 626 // -- rsp[16] : receiver 627 // ----------------------------------- 628 629 __ pop(rcx); 630 __ push(Operand(rsp, 1 * kPointerSize)); // receiver 631 __ push(Operand(rsp, 1 * kPointerSize)); // key 632 __ push(rax); // value 633 __ push(rcx); // return address 634 635 // Do tail-call to runtime routine. 636 __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); 637} 638 639 640void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { 641 // ----------- S t a t e ------------- 642 // -- rax : value 643 // -- rsp[0] : return address 644 // -- rsp[8] : key 645 // -- rsp[16] : receiver 646 // ----------------------------------- 647 Label slow, fast, array, extra, check_pixel_array; 648 649 // Get the receiver from the stack. 650 __ movq(rdx, Operand(rsp, 2 * kPointerSize)); // 2 ~ return address, key 651 // Check that the object isn't a smi. 652 __ JumpIfSmi(rdx, &slow); 653 // Get the map from the receiver. 654 __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset)); 655 // Check that the receiver does not require access checks. We need 656 // to do this because this generic stub does not perform map checks. 657 __ testb(FieldOperand(rcx, Map::kBitFieldOffset), 658 Immediate(1 << Map::kIsAccessCheckNeeded)); 659 __ j(not_zero, &slow); 660 // Get the key from the stack. 661 __ movq(rbx, Operand(rsp, 1 * kPointerSize)); // 1 ~ return address 662 // Check that the key is a smi. 663 __ JumpIfNotSmi(rbx, &slow); 664 665 __ CmpInstanceType(rcx, JS_ARRAY_TYPE); 666 __ j(equal, &array); 667 // Check that the object is some kind of JS object. 668 __ CmpInstanceType(rcx, FIRST_JS_OBJECT_TYPE); 669 __ j(below, &slow); 670 671 // Object case: Check key against length in the elements array. 672 // rax: value 673 // rdx: JSObject 674 // rbx: index (as a smi) 675 __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); 676 // Check that the object is in fast mode (not dictionary). 677 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 678 Heap::kFixedArrayMapRootIndex); 679 __ j(not_equal, &check_pixel_array); 680 // Untag the key (for checking against untagged length in the fixed array). 681 __ SmiToInteger32(rdx, rbx); 682 __ cmpl(rdx, FieldOperand(rcx, Array::kLengthOffset)); 683 // rax: value 684 // rcx: FixedArray 685 // rbx: index (as a smi) 686 __ j(below, &fast); 687 688 // Slow case: call runtime. 689 __ bind(&slow); 690 GenerateRuntimeSetProperty(masm); 691 692 // Check whether the elements is a pixel array. 693 // rax: value 694 // rcx: elements array 695 // rbx: index (as a smi), zero-extended. 696 __ bind(&check_pixel_array); 697 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 698 Heap::kPixelArrayMapRootIndex); 699 __ j(not_equal, &slow); 700 // Check that the value is a smi. If a conversion is needed call into the 701 // runtime to convert and clamp. 702 __ JumpIfNotSmi(rax, &slow); 703 __ SmiToInteger32(rbx, rbx); 704 __ cmpl(rbx, FieldOperand(rcx, PixelArray::kLengthOffset)); 705 __ j(above_equal, &slow); 706 __ movq(rdx, rax); // Save the value. 707 __ SmiToInteger32(rax, rax); 708 { // Clamp the value to [0..255]. 709 Label done; 710 __ testl(rax, Immediate(0xFFFFFF00)); 711 __ j(zero, &done); 712 __ setcc(negative, rax); // 1 if negative, 0 if positive. 713 __ decb(rax); // 0 if negative, 255 if positive. 714 __ bind(&done); 715 } 716 __ movq(rcx, FieldOperand(rcx, PixelArray::kExternalPointerOffset)); 717 __ movb(Operand(rcx, rbx, times_1, 0), rax); 718 __ movq(rax, rdx); // Return the original value. 719 __ ret(0); 720 721 // Extra capacity case: Check if there is extra capacity to 722 // perform the store and update the length. Used for adding one 723 // element to the array by writing to array[array.length]. 724 __ bind(&extra); 725 // rax: value 726 // rdx: JSArray 727 // rcx: FixedArray 728 // rbx: index (as a smi) 729 // flags: smicompare (rdx.length(), rbx) 730 __ j(not_equal, &slow); // do not leave holes in the array 731 __ SmiToInteger64(rbx, rbx); 732 __ cmpl(rbx, FieldOperand(rcx, FixedArray::kLengthOffset)); 733 __ j(above_equal, &slow); 734 // Increment and restore smi-tag. 735 __ Integer64PlusConstantToSmi(rbx, rbx, 1); 736 __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rbx); 737 __ SmiSubConstant(rbx, rbx, Smi::FromInt(1)); 738 __ jmp(&fast); 739 740 // Array case: Get the length and the elements array from the JS 741 // array. Check that the array is in fast mode; if it is the 742 // length is always a smi. 743 __ bind(&array); 744 // rax: value 745 // rdx: JSArray 746 // rbx: index (as a smi) 747 __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); 748 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 749 Heap::kFixedArrayMapRootIndex); 750 __ j(not_equal, &slow); 751 752 // Check the key against the length in the array, compute the 753 // address to store into and fall through to fast case. 754 __ SmiCompare(FieldOperand(rdx, JSArray::kLengthOffset), rbx); 755 __ j(below_equal, &extra); 756 757 // Fast case: Do the store. 758 __ bind(&fast); 759 // rax: value 760 // rcx: FixedArray 761 // rbx: index (as a smi) 762 Label non_smi_value; 763 __ JumpIfNotSmi(rax, &non_smi_value); 764 SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2); 765 __ movq(Operand(rcx, index.reg, index.scale, 766 FixedArray::kHeaderSize - kHeapObjectTag), 767 rax); 768 __ ret(0); 769 __ bind(&non_smi_value); 770 // Slow case that needs to retain rbx for use by RecordWrite. 771 // Update write barrier for the elements array address. 772 SmiIndex index2 = masm->SmiToIndex(kScratchRegister, rbx, kPointerSizeLog2); 773 __ movq(Operand(rcx, index2.reg, index2.scale, 774 FixedArray::kHeaderSize - kHeapObjectTag), 775 rax); 776 __ movq(rdx, rax); 777 __ RecordWriteNonSmi(rcx, 0, rdx, rbx); 778 __ ret(0); 779} 780 781 782void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, 783 ExternalArrayType array_type) { 784 // ----------- S t a t e ------------- 785 // -- rax : value 786 // -- rsp[0] : return address 787 // -- rsp[8] : key 788 // -- rsp[16] : receiver 789 // ----------------------------------- 790 Label slow, check_heap_number; 791 792 // Get the receiver from the stack. 793 __ movq(rdx, Operand(rsp, 2 * kPointerSize)); 794 // Check that the object isn't a smi. 795 __ JumpIfSmi(rdx, &slow); 796 // Get the map from the receiver. 797 __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset)); 798 // Check that the receiver does not require access checks. We need 799 // to do this because this generic stub does not perform map checks. 800 __ testb(FieldOperand(rcx, Map::kBitFieldOffset), 801 Immediate(1 << Map::kIsAccessCheckNeeded)); 802 __ j(not_zero, &slow); 803 // Get the key from the stack. 804 __ movq(rbx, Operand(rsp, 1 * kPointerSize)); // 1 ~ return address 805 // Check that the key is a smi. 806 __ JumpIfNotSmi(rbx, &slow); 807 808 // Check that the object is a JS object. 809 __ CmpInstanceType(rcx, JS_OBJECT_TYPE); 810 __ j(not_equal, &slow); 811 812 // Check that the elements array is the appropriate type of 813 // ExternalArray. 814 // rax: value 815 // rdx: JSObject 816 // rbx: index (as a smi) 817 __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); 818 __ CompareRoot(FieldOperand(rcx, HeapObject::kMapOffset), 819 Heap::RootIndexForExternalArrayType(array_type)); 820 __ j(not_equal, &slow); 821 822 // Check that the index is in range. 823 __ SmiToInteger32(rbx, rbx); // Untag the index. 824 __ cmpl(rbx, FieldOperand(rcx, ExternalArray::kLengthOffset)); 825 // Unsigned comparison catches both negative and too-large values. 826 __ j(above_equal, &slow); 827 828 // Handle both smis and HeapNumbers in the fast path. Go to the 829 // runtime for all other kinds of values. 830 // rax: value 831 // rcx: elements array 832 // rbx: untagged index 833 __ JumpIfNotSmi(rax, &check_heap_number); 834 __ movq(rdx, rax); // Save the value. 835 __ SmiToInteger32(rax, rax); 836 __ movq(rcx, FieldOperand(rcx, ExternalArray::kExternalPointerOffset)); 837 // rcx: base pointer of external storage 838 switch (array_type) { 839 case kExternalByteArray: 840 case kExternalUnsignedByteArray: 841 __ movb(Operand(rcx, rbx, times_1, 0), rax); 842 break; 843 case kExternalShortArray: 844 case kExternalUnsignedShortArray: 845 __ movw(Operand(rcx, rbx, times_2, 0), rax); 846 break; 847 case kExternalIntArray: 848 case kExternalUnsignedIntArray: 849 __ movl(Operand(rcx, rbx, times_4, 0), rax); 850 break; 851 case kExternalFloatArray: 852 // Need to perform int-to-float conversion. 853 __ push(rax); 854 __ fild_s(Operand(rsp, 0)); 855 __ pop(rax); 856 __ fstp_s(Operand(rcx, rbx, times_4, 0)); 857 break; 858 default: 859 UNREACHABLE(); 860 break; 861 } 862 __ movq(rax, rdx); // Return the original value. 863 __ ret(0); 864 865 __ bind(&check_heap_number); 866 __ CmpObjectType(rax, HEAP_NUMBER_TYPE, rdx); 867 __ j(not_equal, &slow); 868 869 // The WebGL specification leaves the behavior of storing NaN and 870 // +/-Infinity into integer arrays basically undefined. For more 871 // reproducible behavior, convert these to zero. 872 __ fld_d(FieldOperand(rax, HeapNumber::kValueOffset)); 873 __ movq(rdx, rax); // Save the value. 874 __ movq(rcx, FieldOperand(rcx, ExternalArray::kExternalPointerOffset)); 875 // rbx: untagged index 876 // rcx: base pointer of external storage 877 // top of FPU stack: value 878 if (array_type == kExternalFloatArray) { 879 __ fstp_s(Operand(rcx, rbx, times_4, 0)); 880 __ movq(rax, rdx); // Return the original value. 881 __ ret(0); 882 } else { 883 // Need to perform float-to-int conversion. 884 // Test the top of the FP stack for NaN. 885 Label is_nan; 886 __ fucomi(0); 887 __ j(parity_even, &is_nan); 888 889 __ push(rax); // Make room on stack 890 __ fistp_d(Operand(rsp, 0)); 891 __ pop(rax); 892 // rax: untagged integer value 893 switch (array_type) { 894 case kExternalByteArray: 895 case kExternalUnsignedByteArray: 896 __ movb(Operand(rcx, rbx, times_1, 0), rax); 897 break; 898 case kExternalShortArray: 899 case kExternalUnsignedShortArray: 900 __ movw(Operand(rcx, rbx, times_2, 0), rax); 901 break; 902 case kExternalIntArray: 903 case kExternalUnsignedIntArray: { 904 // We also need to explicitly check for +/-Infinity. These are 905 // converted to MIN_INT, but we need to be careful not to 906 // confuse with legal uses of MIN_INT. 907 Label not_infinity; 908 // This test would apparently detect both NaN and Infinity, 909 // but we've already checked for NaN using the FPU hardware 910 // above. 911 __ movzxwq(rdi, FieldOperand(rdx, HeapNumber::kValueOffset + 6)); 912 __ and_(rdi, Immediate(0x7FF0)); 913 __ cmpw(rdi, Immediate(0x7FF0)); 914 __ j(not_equal, ¬_infinity); 915 __ movq(rax, Immediate(0)); 916 __ bind(¬_infinity); 917 __ movl(Operand(rcx, rbx, times_4, 0), rax); 918 break; 919 } 920 default: 921 UNREACHABLE(); 922 break; 923 } 924 __ movq(rax, rdx); // Return the original value. 925 __ ret(0); 926 927 __ bind(&is_nan); 928 __ ffree(); 929 __ fincstp(); 930 __ movq(rax, Immediate(0)); 931 switch (array_type) { 932 case kExternalByteArray: 933 case kExternalUnsignedByteArray: 934 __ movb(Operand(rcx, rbx, times_1, 0), rax); 935 break; 936 case kExternalShortArray: 937 case kExternalUnsignedShortArray: 938 __ movw(Operand(rcx, rbx, times_2, 0), rax); 939 break; 940 case kExternalIntArray: 941 case kExternalUnsignedIntArray: 942 __ movl(Operand(rcx, rbx, times_4, 0), rax); 943 break; 944 default: 945 UNREACHABLE(); 946 break; 947 } 948 __ movq(rax, rdx); // Return the original value. 949 __ ret(0); 950 } 951 952 // Slow case: call runtime. 953 __ bind(&slow); 954 GenerateRuntimeSetProperty(masm); 955} 956 957 958void CallIC::GenerateMiss(MacroAssembler* masm, int argc) { 959 // ----------- S t a t e ------------- 960 // rcx : function name 961 // rsp[0] : return address 962 // rsp[8] : argument argc 963 // rsp[16] : argument argc - 1 964 // ... 965 // rsp[argc * 8] : argument 1 966 // rsp[(argc + 1) * 8] : argument 0 = receiver 967 // ----------------------------------- 968 // Get the receiver of the function from the stack; 1 ~ return address. 969 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); 970 971 // Enter an internal frame. 972 __ EnterInternalFrame(); 973 974 // Push the receiver and the name of the function. 975 __ push(rdx); 976 __ push(rcx); 977 978 // Call the entry. 979 CEntryStub stub(1); 980 __ movq(rax, Immediate(2)); 981 __ movq(rbx, ExternalReference(IC_Utility(kCallIC_Miss))); 982 __ CallStub(&stub); 983 984 // Move result to rdi and exit the internal frame. 985 __ movq(rdi, rax); 986 __ LeaveInternalFrame(); 987 988 // Check if the receiver is a global object of some sort. 989 Label invoke, global; 990 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver 991 __ JumpIfSmi(rdx, &invoke); 992 __ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx); 993 __ j(equal, &global); 994 __ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE); 995 __ j(not_equal, &invoke); 996 997 // Patch the receiver on the stack. 998 __ bind(&global); 999 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); 1000 __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); 1001 1002 // Invoke the function. 1003 ParameterCount actual(argc); 1004 __ bind(&invoke); 1005 __ InvokeFunction(rdi, actual, JUMP_FUNCTION); 1006} 1007 1008 1009// Defined in ic.cc. 1010Object* CallIC_Miss(Arguments args); 1011 1012void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { 1013 // ----------- S t a t e ------------- 1014 // rcx : function name 1015 // rsp[0] : return address 1016 // rsp[8] : argument argc 1017 // rsp[16] : argument argc - 1 1018 // ... 1019 // rsp[argc * 8] : argument 1 1020 // rsp[(argc + 1) * 8] : argument 0 = receiver 1021 // ----------------------------------- 1022 Label number, non_number, non_string, boolean, probe, miss; 1023 1024 // Get the receiver of the function from the stack; 1 ~ return address. 1025 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); 1026 1027 // Probe the stub cache. 1028 Code::Flags flags = 1029 Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); 1030 StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, rax); 1031 1032 // If the stub cache probing failed, the receiver might be a value. 1033 // For value objects, we use the map of the prototype objects for 1034 // the corresponding JSValue for the cache and that is what we need 1035 // to probe. 1036 // 1037 // Check for number. 1038 __ JumpIfSmi(rdx, &number); 1039 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rbx); 1040 __ j(not_equal, &non_number); 1041 __ bind(&number); 1042 StubCompiler::GenerateLoadGlobalFunctionPrototype( 1043 masm, Context::NUMBER_FUNCTION_INDEX, rdx); 1044 __ jmp(&probe); 1045 1046 // Check for string. 1047 __ bind(&non_number); 1048 __ CmpInstanceType(rbx, FIRST_NONSTRING_TYPE); 1049 __ j(above_equal, &non_string); 1050 StubCompiler::GenerateLoadGlobalFunctionPrototype( 1051 masm, Context::STRING_FUNCTION_INDEX, rdx); 1052 __ jmp(&probe); 1053 1054 // Check for boolean. 1055 __ bind(&non_string); 1056 __ CompareRoot(rdx, Heap::kTrueValueRootIndex); 1057 __ j(equal, &boolean); 1058 __ CompareRoot(rdx, Heap::kFalseValueRootIndex); 1059 __ j(not_equal, &miss); 1060 __ bind(&boolean); 1061 StubCompiler::GenerateLoadGlobalFunctionPrototype( 1062 masm, Context::BOOLEAN_FUNCTION_INDEX, rdx); 1063 1064 // Probe the stub cache for the value object. 1065 __ bind(&probe); 1066 StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, no_reg); 1067 1068 // Cache miss: Jump to runtime. 1069 __ bind(&miss); 1070 GenerateMiss(masm, argc); 1071} 1072 1073 1074static void GenerateNormalHelper(MacroAssembler* masm, 1075 int argc, 1076 bool is_global_object, 1077 Label* miss) { 1078 // ----------- S t a t e ------------- 1079 // rcx : function name 1080 // rdx : receiver 1081 // rsp[0] : return address 1082 // rsp[8] : argument argc 1083 // rsp[16] : argument argc - 1 1084 // ... 1085 // rsp[argc * 8] : argument 1 1086 // rsp[(argc + 1) * 8] : argument 0 = receiver 1087 // ----------------------------------- 1088 // Search dictionary - put result in register rdx. 1089 GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx, CHECK_DICTIONARY); 1090 1091 // Move the result to register rdi and check that it isn't a smi. 1092 __ movq(rdi, rdx); 1093 __ JumpIfSmi(rdx, miss); 1094 1095 // Check that the value is a JavaScript function. 1096 __ CmpObjectType(rdx, JS_FUNCTION_TYPE, rdx); 1097 __ j(not_equal, miss); 1098 1099 // Patch the receiver with the global proxy if necessary. 1100 if (is_global_object) { 1101 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); 1102 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); 1103 __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); 1104 } 1105 1106 // Invoke the function. 1107 ParameterCount actual(argc); 1108 __ InvokeFunction(rdi, actual, JUMP_FUNCTION); 1109} 1110 1111 1112void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { 1113 // ----------- S t a t e ------------- 1114 // rcx : function name 1115 // rsp[0] : return address 1116 // rsp[8] : argument argc 1117 // rsp[16] : argument argc - 1 1118 // ... 1119 // rsp[argc * 8] : argument 1 1120 // rsp[(argc + 1) * 8] : argument 0 = receiver 1121 // ----------------------------------- 1122 Label miss, global_object, non_global_object; 1123 1124 // Get the receiver of the function from the stack. 1125 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); 1126 1127 // Check that the receiver isn't a smi. 1128 __ JumpIfSmi(rdx, &miss); 1129 1130 // Check that the receiver is a valid JS object. 1131 // Because there are so many map checks and type checks, do not 1132 // use CmpObjectType, but load map and type into registers. 1133 __ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset)); 1134 __ movb(rax, FieldOperand(rbx, Map::kInstanceTypeOffset)); 1135 __ cmpb(rax, Immediate(FIRST_JS_OBJECT_TYPE)); 1136 __ j(below, &miss); 1137 1138 // If this assert fails, we have to check upper bound too. 1139 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); 1140 1141 // Check for access to global object. 1142 __ cmpb(rax, Immediate(JS_GLOBAL_OBJECT_TYPE)); 1143 __ j(equal, &global_object); 1144 __ cmpb(rax, Immediate(JS_BUILTINS_OBJECT_TYPE)); 1145 __ j(not_equal, &non_global_object); 1146 1147 // Accessing global object: Load and invoke. 1148 __ bind(&global_object); 1149 // Check that the global object does not require access checks. 1150 __ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset)); 1151 __ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded)); 1152 __ j(not_equal, &miss); 1153 GenerateNormalHelper(masm, argc, true, &miss); 1154 1155 // Accessing non-global object: Check for access to global proxy. 1156 Label global_proxy, invoke; 1157 __ bind(&non_global_object); 1158 __ cmpb(rax, Immediate(JS_GLOBAL_PROXY_TYPE)); 1159 __ j(equal, &global_proxy); 1160 // Check that the non-global, non-global-proxy object does not 1161 // require access checks. 1162 __ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset)); 1163 __ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded)); 1164 __ j(not_equal, &miss); 1165 __ bind(&invoke); 1166 GenerateNormalHelper(masm, argc, false, &miss); 1167 1168 // Global object proxy access: Check access rights. 1169 __ bind(&global_proxy); 1170 __ CheckAccessGlobalProxy(rdx, rax, &miss); 1171 __ jmp(&invoke); 1172 1173 // Cache miss: Jump to runtime. 1174 __ bind(&miss); 1175 GenerateMiss(masm, argc); 1176} 1177 1178 1179// The offset from the inlined patch site to the start of the 1180// inlined load instruction. 1181const int LoadIC::kOffsetToLoadInstruction = 20; 1182 1183 1184void LoadIC::ClearInlinedVersion(Address address) { 1185 // Reset the map check of the inlined inobject property load (if 1186 // present) to guarantee failure by holding an invalid map (the null 1187 // value). The offset can be patched to anything. 1188 PatchInlinedLoad(address, Heap::null_value(), kMaxInt); 1189} 1190 1191 1192void LoadIC::GenerateMiss(MacroAssembler* masm) { 1193 // ----------- S t a t e ------------- 1194 // -- rcx : name 1195 // -- rsp[0] : return address 1196 // -- rsp[8] : receiver 1197 // ----------------------------------- 1198 1199 __ pop(rbx); 1200 __ push(Operand(rsp, 0)); // receiver 1201 __ push(rcx); // name 1202 __ push(rbx); // return address 1203 1204 // Perform tail call to the entry. 1205 __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1); 1206} 1207 1208 1209void LoadIC::GenerateArrayLength(MacroAssembler* masm) { 1210 // ----------- S t a t e ------------- 1211 // -- rcx : name 1212 // -- rsp[0] : return address 1213 // -- rsp[8] : receiver 1214 // ----------------------------------- 1215 Label miss; 1216 1217 __ movq(rax, Operand(rsp, kPointerSize)); 1218 1219 StubCompiler::GenerateLoadArrayLength(masm, rax, rdx, &miss); 1220 __ bind(&miss); 1221 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 1222} 1223 1224 1225void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { 1226 // ----------- S t a t e ------------- 1227 // -- rcx : name 1228 // -- rsp[0] : return address 1229 // -- rsp[8] : receiver 1230 // ----------------------------------- 1231 Label miss; 1232 1233 __ movq(rax, Operand(rsp, kPointerSize)); 1234 1235 StubCompiler::GenerateLoadFunctionPrototype(masm, rax, rdx, rbx, &miss); 1236 __ bind(&miss); 1237 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 1238} 1239 1240 1241void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { 1242 // ----------- S t a t e ------------- 1243 // -- rcx : name 1244 // -- rsp[0] : return address 1245 // -- rsp[8] : receiver 1246 // ----------------------------------- 1247 1248 __ movq(rax, Operand(rsp, kPointerSize)); 1249 1250 // Probe the stub cache. 1251 Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, 1252 NOT_IN_LOOP, 1253 MONOMORPHIC); 1254 StubCache::GenerateProbe(masm, flags, rax, rcx, rbx, rdx); 1255 1256 // Cache miss: Jump to runtime. 1257 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 1258} 1259 1260 1261void LoadIC::GenerateNormal(MacroAssembler* masm) { 1262 // ----------- S t a t e ------------- 1263 // -- rcx : name 1264 // -- rsp[0] : return address 1265 // -- rsp[8] : receiver 1266 // ----------------------------------- 1267 Label miss, probe, global; 1268 1269 __ movq(rax, Operand(rsp, kPointerSize)); 1270 1271 // Check that the receiver isn't a smi. 1272 __ JumpIfSmi(rax, &miss); 1273 1274 // Check that the receiver is a valid JS object. 1275 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx); 1276 __ j(below, &miss); 1277 1278 // If this assert fails, we have to check upper bound too. 1279 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); 1280 1281 // Check for access to global object (unlikely). 1282 __ CmpInstanceType(rbx, JS_GLOBAL_PROXY_TYPE); 1283 __ j(equal, &global); 1284 1285 // Check for non-global object that requires access check. 1286 __ testl(FieldOperand(rbx, Map::kBitFieldOffset), 1287 Immediate(1 << Map::kIsAccessCheckNeeded)); 1288 __ j(not_zero, &miss); 1289 1290 // Search the dictionary placing the result in rax. 1291 __ bind(&probe); 1292 GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx, rcx, CHECK_DICTIONARY); 1293 __ ret(0); 1294 1295 // Global object access: Check access rights. 1296 __ bind(&global); 1297 __ CheckAccessGlobalProxy(rax, rdx, &miss); 1298 __ jmp(&probe); 1299 1300 // Cache miss: Restore receiver from stack and jump to runtime. 1301 __ bind(&miss); 1302 __ movq(rax, Operand(rsp, 1 * kPointerSize)); 1303 GenerateMiss(masm); 1304} 1305 1306 1307void LoadIC::GenerateStringLength(MacroAssembler* masm) { 1308 // ----------- S t a t e ------------- 1309 // -- rcx : name 1310 // -- rsp[0] : return address 1311 // -- rsp[8] : receiver 1312 // ----------------------------------- 1313 Label miss; 1314 1315 __ movq(rax, Operand(rsp, kPointerSize)); 1316 1317 StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss); 1318 __ bind(&miss); 1319 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 1320} 1321 1322 1323bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { 1324 // The address of the instruction following the call. 1325 Address test_instruction_address = 1326 address + Assembler::kCallTargetAddressOffset; 1327 // If the instruction following the call is not a test eax, nothing 1328 // was inlined. 1329 if (*test_instruction_address != kTestEaxByte) return false; 1330 1331 Address delta_address = test_instruction_address + 1; 1332 // The delta to the start of the map check instruction. 1333 int delta = *reinterpret_cast<int*>(delta_address); 1334 1335 // The map address is the last 8 bytes of the 10-byte 1336 // immediate move instruction, so we add 2 to get the 1337 // offset to the last 8 bytes. 1338 Address map_address = test_instruction_address + delta + 2; 1339 *(reinterpret_cast<Object**>(map_address)) = map; 1340 1341 // The offset is in the 32-bit displacement of a seven byte 1342 // memory-to-register move instruction (REX.W 0x88 ModR/M disp32), 1343 // so we add 3 to get the offset of the displacement. 1344 Address offset_address = 1345 test_instruction_address + delta + kOffsetToLoadInstruction + 3; 1346 *reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag; 1347 return true; 1348} 1349 1350 1351void StoreIC::GenerateMiss(MacroAssembler* masm) { 1352 // ----------- S t a t e ------------- 1353 // -- rax : value 1354 // -- rcx : name 1355 // -- rdx : receiver 1356 // -- rsp[0] : return address 1357 // ----------------------------------- 1358 1359 __ pop(rbx); 1360 __ push(rdx); // receiver 1361 __ push(rcx); // name 1362 __ push(rax); // value 1363 __ push(rbx); // return address 1364 1365 // Perform tail call to the entry. 1366 __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1); 1367} 1368 1369 1370void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { 1371 // ----------- S t a t e ------------- 1372 // -- rax : value 1373 // -- rcx : name 1374 // -- rdx : receiver 1375 // -- rsp[0] : return address 1376 // ----------------------------------- 1377 1378 // Get the receiver from the stack and probe the stub cache. 1379 Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, 1380 NOT_IN_LOOP, 1381 MONOMORPHIC); 1382 StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, no_reg); 1383 1384 // Cache miss: Jump to runtime. 1385 GenerateMiss(masm); 1386} 1387 1388 1389#undef __ 1390 1391 1392} } // namespace v8::internal 1393