1// Copyright 2012 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#if defined(V8_TARGET_ARCH_IA32) 31 32#include "codegen.h" 33#include "ic-inl.h" 34#include "runtime.h" 35#include "stub-cache.h" 36 37namespace v8 { 38namespace internal { 39 40// ---------------------------------------------------------------------------- 41// Static IC stub generators. 42// 43 44#define __ ACCESS_MASM(masm) 45 46 47static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, 48 Register type, 49 Label* global_object) { 50 // Register usage: 51 // type: holds the receiver instance type on entry. 52 __ cmp(type, JS_GLOBAL_OBJECT_TYPE); 53 __ j(equal, global_object); 54 __ cmp(type, JS_BUILTINS_OBJECT_TYPE); 55 __ j(equal, global_object); 56 __ cmp(type, JS_GLOBAL_PROXY_TYPE); 57 __ j(equal, global_object); 58} 59 60 61// Generated code falls through if the receiver is a regular non-global 62// JS object with slow properties and no interceptors. 63static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm, 64 Register receiver, 65 Register r0, 66 Register r1, 67 Label* miss) { 68 // Register usage: 69 // receiver: holds the receiver on entry and is unchanged. 70 // r0: used to hold receiver instance type. 71 // Holds the property dictionary on fall through. 72 // r1: used to hold receivers map. 73 74 // Check that the receiver isn't a smi. 75 __ JumpIfSmi(receiver, miss); 76 77 // Check that the receiver is a valid JS object. 78 __ mov(r1, FieldOperand(receiver, HeapObject::kMapOffset)); 79 __ movzx_b(r0, FieldOperand(r1, Map::kInstanceTypeOffset)); 80 __ cmp(r0, FIRST_SPEC_OBJECT_TYPE); 81 __ j(below, miss); 82 83 // If this assert fails, we have to check upper bound too. 84 STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE); 85 86 GenerateGlobalInstanceTypeCheck(masm, r0, miss); 87 88 // Check for non-global object that requires access check. 89 __ test_b(FieldOperand(r1, Map::kBitFieldOffset), 90 (1 << Map::kIsAccessCheckNeeded) | 91 (1 << Map::kHasNamedInterceptor)); 92 __ j(not_zero, miss); 93 94 __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset)); 95 __ CheckMap(r0, FACTORY->hash_table_map(), miss, DONT_DO_SMI_CHECK); 96} 97 98 99// Helper function used to load a property from a dictionary backing 100// storage. This function may fail to load a property even though it is 101// in the dictionary, so code at miss_label must always call a backup 102// property load that is complete. This function is safe to call if 103// name is not a symbol, and will jump to the miss_label in that 104// case. The generated code assumes that the receiver has slow 105// properties, is not a global object and does not have interceptors. 106static void GenerateDictionaryLoad(MacroAssembler* masm, 107 Label* miss_label, 108 Register elements, 109 Register name, 110 Register r0, 111 Register r1, 112 Register result) { 113 // Register use: 114 // 115 // elements - holds the property dictionary on entry and is unchanged. 116 // 117 // name - holds the name of the property on entry and is unchanged. 118 // 119 // Scratch registers: 120 // 121 // r0 - used for the index into the property dictionary 122 // 123 // r1 - used to hold the capacity of the property dictionary. 124 // 125 // result - holds the result on exit. 126 127 Label done; 128 129 // Probe the dictionary. 130 StringDictionaryLookupStub::GeneratePositiveLookup(masm, 131 miss_label, 132 &done, 133 elements, 134 name, 135 r0, 136 r1); 137 138 // If probing finds an entry in the dictionary, r0 contains the 139 // index into the dictionary. Check that the value is a normal 140 // property. 141 __ bind(&done); 142 const int kElementsStartOffset = 143 StringDictionary::kHeaderSize + 144 StringDictionary::kElementsStartIndex * kPointerSize; 145 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; 146 __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag), 147 Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize)); 148 __ j(not_zero, miss_label); 149 150 // Get the value at the masked, scaled index. 151 const int kValueOffset = kElementsStartOffset + kPointerSize; 152 __ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag)); 153} 154 155 156// Helper function used to store a property to a dictionary backing 157// storage. This function may fail to store a property eventhough it 158// is in the dictionary, so code at miss_label must always call a 159// backup property store that is complete. This function is safe to 160// call if name is not a symbol, and will jump to the miss_label in 161// that case. The generated code assumes that the receiver has slow 162// properties, is not a global object and does not have interceptors. 163static void GenerateDictionaryStore(MacroAssembler* masm, 164 Label* miss_label, 165 Register elements, 166 Register name, 167 Register value, 168 Register r0, 169 Register r1) { 170 // Register use: 171 // 172 // elements - holds the property dictionary on entry and is clobbered. 173 // 174 // name - holds the name of the property on entry and is unchanged. 175 // 176 // value - holds the value to store and is unchanged. 177 // 178 // r0 - used for index into the property dictionary and is clobbered. 179 // 180 // r1 - used to hold the capacity of the property dictionary and is clobbered. 181 Label done; 182 183 184 // Probe the dictionary. 185 StringDictionaryLookupStub::GeneratePositiveLookup(masm, 186 miss_label, 187 &done, 188 elements, 189 name, 190 r0, 191 r1); 192 193 // If probing finds an entry in the dictionary, r0 contains the 194 // index into the dictionary. Check that the value is a normal 195 // property that is not read only. 196 __ bind(&done); 197 const int kElementsStartOffset = 198 StringDictionary::kHeaderSize + 199 StringDictionary::kElementsStartIndex * kPointerSize; 200 const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; 201 const int kTypeAndReadOnlyMask = 202 (PropertyDetails::TypeField::kMask | 203 PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; 204 __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag), 205 Immediate(kTypeAndReadOnlyMask)); 206 __ j(not_zero, miss_label); 207 208 // Store the value at the masked, scaled index. 209 const int kValueOffset = kElementsStartOffset + kPointerSize; 210 __ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag)); 211 __ mov(Operand(r0, 0), value); 212 213 // Update write barrier. Make sure not to clobber the value. 214 __ mov(r1, value); 215 __ RecordWrite(elements, r0, r1, kDontSaveFPRegs); 216} 217 218 219void LoadIC::GenerateArrayLength(MacroAssembler* masm) { 220 // ----------- S t a t e ------------- 221 // -- eax : receiver 222 // -- ecx : name 223 // -- esp[0] : return address 224 // ----------------------------------- 225 Label miss; 226 227 StubCompiler::GenerateLoadArrayLength(masm, eax, edx, &miss); 228 __ bind(&miss); 229 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 230} 231 232 233void LoadIC::GenerateStringLength(MacroAssembler* masm, 234 bool support_wrappers) { 235 // ----------- S t a t e ------------- 236 // -- eax : receiver 237 // -- ecx : name 238 // -- esp[0] : return address 239 // ----------------------------------- 240 Label miss; 241 242 StubCompiler::GenerateLoadStringLength(masm, eax, edx, ebx, &miss, 243 support_wrappers); 244 __ bind(&miss); 245 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 246} 247 248 249void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { 250 // ----------- S t a t e ------------- 251 // -- eax : receiver 252 // -- ecx : name 253 // -- esp[0] : return address 254 // ----------------------------------- 255 Label miss; 256 257 StubCompiler::GenerateLoadFunctionPrototype(masm, eax, edx, ebx, &miss); 258 __ bind(&miss); 259 StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); 260} 261 262 263// Checks the receiver for special cases (value type, slow case bits). 264// Falls through for regular JS object. 265static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, 266 Register receiver, 267 Register map, 268 int interceptor_bit, 269 Label* slow) { 270 // Register use: 271 // receiver - holds the receiver and is unchanged. 272 // Scratch registers: 273 // map - used to hold the map of the receiver. 274 275 // Check that the object isn't a smi. 276 __ JumpIfSmi(receiver, slow); 277 278 // Get the map of the receiver. 279 __ mov(map, FieldOperand(receiver, HeapObject::kMapOffset)); 280 281 // Check bit field. 282 __ test_b(FieldOperand(map, Map::kBitFieldOffset), 283 (1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)); 284 __ j(not_zero, slow); 285 // Check that the object is some kind of JS object EXCEPT JS Value type. 286 // In the case that the object is a value-wrapper object, 287 // we enter the runtime system to make sure that indexing 288 // into string objects works as intended. 289 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); 290 291 __ CmpInstanceType(map, JS_OBJECT_TYPE); 292 __ j(below, slow); 293} 294 295 296// Loads an indexed element from a fast case array. 297// If not_fast_array is NULL, doesn't perform the elements map check. 298static void GenerateFastArrayLoad(MacroAssembler* masm, 299 Register receiver, 300 Register key, 301 Register scratch, 302 Register result, 303 Label* not_fast_array, 304 Label* out_of_range) { 305 // Register use: 306 // receiver - holds the receiver and is unchanged. 307 // key - holds the key and is unchanged (must be a smi). 308 // Scratch registers: 309 // scratch - used to hold elements of the receiver and the loaded value. 310 // result - holds the result on exit if the load succeeds and 311 // we fall through. 312 313 __ mov(scratch, FieldOperand(receiver, JSObject::kElementsOffset)); 314 if (not_fast_array != NULL) { 315 // Check that the object is in fast mode and writable. 316 __ CheckMap(scratch, 317 FACTORY->fixed_array_map(), 318 not_fast_array, 319 DONT_DO_SMI_CHECK); 320 } else { 321 __ AssertFastElements(scratch); 322 } 323 // Check that the key (index) is within bounds. 324 __ cmp(key, FieldOperand(scratch, FixedArray::kLengthOffset)); 325 __ j(above_equal, out_of_range); 326 // Fast case: Do the load. 327 STATIC_ASSERT((kPointerSize == 4) && (kSmiTagSize == 1) && (kSmiTag == 0)); 328 __ mov(scratch, FieldOperand(scratch, key, times_2, FixedArray::kHeaderSize)); 329 __ cmp(scratch, Immediate(FACTORY->the_hole_value())); 330 // In case the loaded value is the_hole we have to consult GetProperty 331 // to ensure the prototype chain is searched. 332 __ j(equal, out_of_range); 333 if (!result.is(scratch)) { 334 __ mov(result, scratch); 335 } 336} 337 338 339// Checks whether a key is an array index string or a symbol string. 340// Falls through if the key is a symbol. 341static void GenerateKeyStringCheck(MacroAssembler* masm, 342 Register key, 343 Register map, 344 Register hash, 345 Label* index_string, 346 Label* not_symbol) { 347 // Register use: 348 // key - holds the key and is unchanged. Assumed to be non-smi. 349 // Scratch registers: 350 // map - used to hold the map of the key. 351 // hash - used to hold the hash of the key. 352 __ CmpObjectType(key, FIRST_NONSTRING_TYPE, map); 353 __ j(above_equal, not_symbol); 354 355 // Is the string an array index, with cached numeric value? 356 __ mov(hash, FieldOperand(key, String::kHashFieldOffset)); 357 __ test(hash, Immediate(String::kContainsCachedArrayIndexMask)); 358 __ j(zero, index_string); 359 360 // Is the string a symbol? 361 STATIC_ASSERT(kSymbolTag != 0); 362 __ test_b(FieldOperand(map, Map::kInstanceTypeOffset), kIsSymbolMask); 363 __ j(zero, not_symbol); 364} 365 366 367static Operand GenerateMappedArgumentsLookup(MacroAssembler* masm, 368 Register object, 369 Register key, 370 Register scratch1, 371 Register scratch2, 372 Label* unmapped_case, 373 Label* slow_case) { 374 Heap* heap = masm->isolate()->heap(); 375 Factory* factory = masm->isolate()->factory(); 376 377 // Check that the receiver is a JSObject. Because of the elements 378 // map check later, we do not need to check for interceptors or 379 // whether it requires access checks. 380 __ JumpIfSmi(object, slow_case); 381 // Check that the object is some kind of JSObject. 382 __ CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, scratch1); 383 __ j(below, slow_case); 384 385 // Check that the key is a positive smi. 386 __ test(key, Immediate(0x80000001)); 387 __ j(not_zero, slow_case); 388 389 // Load the elements into scratch1 and check its map. 390 Handle<Map> arguments_map(heap->non_strict_arguments_elements_map()); 391 __ mov(scratch1, FieldOperand(object, JSObject::kElementsOffset)); 392 __ CheckMap(scratch1, arguments_map, slow_case, DONT_DO_SMI_CHECK); 393 394 // Check if element is in the range of mapped arguments. If not, jump 395 // to the unmapped lookup with the parameter map in scratch1. 396 __ mov(scratch2, FieldOperand(scratch1, FixedArray::kLengthOffset)); 397 __ sub(scratch2, Immediate(Smi::FromInt(2))); 398 __ cmp(key, scratch2); 399 __ j(above_equal, unmapped_case); 400 401 // Load element index and check whether it is the hole. 402 const int kHeaderSize = FixedArray::kHeaderSize + 2 * kPointerSize; 403 __ mov(scratch2, FieldOperand(scratch1, 404 key, 405 times_half_pointer_size, 406 kHeaderSize)); 407 __ cmp(scratch2, factory->the_hole_value()); 408 __ j(equal, unmapped_case); 409 410 // Load value from context and return it. We can reuse scratch1 because 411 // we do not jump to the unmapped lookup (which requires the parameter 412 // map in scratch1). 413 const int kContextOffset = FixedArray::kHeaderSize; 414 __ mov(scratch1, FieldOperand(scratch1, kContextOffset)); 415 return FieldOperand(scratch1, 416 scratch2, 417 times_half_pointer_size, 418 Context::kHeaderSize); 419} 420 421 422static Operand GenerateUnmappedArgumentsLookup(MacroAssembler* masm, 423 Register key, 424 Register parameter_map, 425 Register scratch, 426 Label* slow_case) { 427 // Element is in arguments backing store, which is referenced by the 428 // second element of the parameter_map. 429 const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize; 430 Register backing_store = parameter_map; 431 __ mov(backing_store, FieldOperand(parameter_map, kBackingStoreOffset)); 432 Handle<Map> fixed_array_map(masm->isolate()->heap()->fixed_array_map()); 433 __ CheckMap(backing_store, fixed_array_map, slow_case, DONT_DO_SMI_CHECK); 434 __ mov(scratch, FieldOperand(backing_store, FixedArray::kLengthOffset)); 435 __ cmp(key, scratch); 436 __ j(greater_equal, slow_case); 437 return FieldOperand(backing_store, 438 key, 439 times_half_pointer_size, 440 FixedArray::kHeaderSize); 441} 442 443 444void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { 445 // ----------- S t a t e ------------- 446 // -- eax : key 447 // -- edx : receiver 448 // -- esp[0] : return address 449 // ----------------------------------- 450 Label slow, check_string, index_smi, index_string, property_array_property; 451 Label probe_dictionary, check_number_dictionary; 452 453 // Check that the key is a smi. 454 __ JumpIfNotSmi(eax, &check_string); 455 __ bind(&index_smi); 456 // Now the key is known to be a smi. This place is also jumped to from 457 // where a numeric string is converted to a smi. 458 459 GenerateKeyedLoadReceiverCheck( 460 masm, edx, ecx, Map::kHasIndexedInterceptor, &slow); 461 462 // Check the receiver's map to see if it has fast elements. 463 __ CheckFastElements(ecx, &check_number_dictionary); 464 465 GenerateFastArrayLoad(masm, 466 edx, 467 eax, 468 ecx, 469 eax, 470 NULL, 471 &slow); 472 Isolate* isolate = masm->isolate(); 473 Counters* counters = isolate->counters(); 474 __ IncrementCounter(counters->keyed_load_generic_smi(), 1); 475 __ ret(0); 476 __ bind(&check_number_dictionary); 477 __ mov(ebx, eax); 478 __ SmiUntag(ebx); 479 __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); 480 481 // Check whether the elements is a number dictionary. 482 // edx: receiver 483 // ebx: untagged index 484 // eax: key 485 // ecx: elements 486 __ CheckMap(ecx, 487 isolate->factory()->hash_table_map(), 488 &slow, 489 DONT_DO_SMI_CHECK); 490 Label slow_pop_receiver; 491 // Push receiver on the stack to free up a register for the dictionary 492 // probing. 493 __ push(edx); 494 __ LoadFromNumberDictionary(&slow_pop_receiver, 495 ecx, 496 eax, 497 ebx, 498 edx, 499 edi, 500 eax); 501 // Pop receiver before returning. 502 __ pop(edx); 503 __ ret(0); 504 505 __ bind(&slow_pop_receiver); 506 // Pop the receiver from the stack and jump to runtime. 507 __ pop(edx); 508 509 __ bind(&slow); 510 // Slow case: jump to runtime. 511 // edx: receiver 512 // eax: key 513 __ IncrementCounter(counters->keyed_load_generic_slow(), 1); 514 GenerateRuntimeGetProperty(masm); 515 516 __ bind(&check_string); 517 GenerateKeyStringCheck(masm, eax, ecx, ebx, &index_string, &slow); 518 519 GenerateKeyedLoadReceiverCheck( 520 masm, edx, ecx, Map::kHasNamedInterceptor, &slow); 521 522 // If the receiver is a fast-case object, check the keyed lookup 523 // cache. Otherwise probe the dictionary. 524 __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset)); 525 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), 526 Immediate(isolate->factory()->hash_table_map())); 527 __ j(equal, &probe_dictionary); 528 529 // Load the map of the receiver, compute the keyed lookup cache hash 530 // based on 32 bits of the map pointer and the string hash. 531 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); 532 __ mov(ecx, ebx); 533 __ shr(ecx, KeyedLookupCache::kMapHashShift); 534 __ mov(edi, FieldOperand(eax, String::kHashFieldOffset)); 535 __ shr(edi, String::kHashShift); 536 __ xor_(ecx, edi); 537 __ and_(ecx, KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask); 538 539 // Load the key (consisting of map and symbol) from the cache and 540 // check for match. 541 Label load_in_object_property; 542 static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket; 543 Label hit_on_nth_entry[kEntriesPerBucket]; 544 ExternalReference cache_keys = 545 ExternalReference::keyed_lookup_cache_keys(masm->isolate()); 546 547 for (int i = 0; i < kEntriesPerBucket - 1; i++) { 548 Label try_next_entry; 549 __ mov(edi, ecx); 550 __ shl(edi, kPointerSizeLog2 + 1); 551 if (i != 0) { 552 __ add(edi, Immediate(kPointerSize * i * 2)); 553 } 554 __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys)); 555 __ j(not_equal, &try_next_entry); 556 __ add(edi, Immediate(kPointerSize)); 557 __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys)); 558 __ j(equal, &hit_on_nth_entry[i]); 559 __ bind(&try_next_entry); 560 } 561 562 __ lea(edi, Operand(ecx, 1)); 563 __ shl(edi, kPointerSizeLog2 + 1); 564 __ add(edi, Immediate(kPointerSize * (kEntriesPerBucket - 1) * 2)); 565 __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys)); 566 __ j(not_equal, &slow); 567 __ add(edi, Immediate(kPointerSize)); 568 __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys)); 569 __ j(not_equal, &slow); 570 571 // Get field offset. 572 // edx : receiver 573 // ebx : receiver's map 574 // eax : key 575 // ecx : lookup cache index 576 ExternalReference cache_field_offsets = 577 ExternalReference::keyed_lookup_cache_field_offsets(masm->isolate()); 578 579 // Hit on nth entry. 580 for (int i = kEntriesPerBucket - 1; i >= 0; i--) { 581 __ bind(&hit_on_nth_entry[i]); 582 if (i != 0) { 583 __ add(ecx, Immediate(i)); 584 } 585 __ mov(edi, 586 Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets)); 587 __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset)); 588 __ sub(edi, ecx); 589 __ j(above_equal, &property_array_property); 590 if (i != 0) { 591 __ jmp(&load_in_object_property); 592 } 593 } 594 595 // Load in-object property. 596 __ bind(&load_in_object_property); 597 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); 598 __ add(ecx, edi); 599 __ mov(eax, FieldOperand(edx, ecx, times_pointer_size, 0)); 600 __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1); 601 __ ret(0); 602 603 // Load property array property. 604 __ bind(&property_array_property); 605 __ mov(eax, FieldOperand(edx, JSObject::kPropertiesOffset)); 606 __ mov(eax, FieldOperand(eax, edi, times_pointer_size, 607 FixedArray::kHeaderSize)); 608 __ IncrementCounter(counters->keyed_load_generic_lookup_cache(), 1); 609 __ ret(0); 610 611 // Do a quick inline probe of the receiver's dictionary, if it 612 // exists. 613 __ bind(&probe_dictionary); 614 615 __ mov(ecx, FieldOperand(edx, JSObject::kMapOffset)); 616 __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); 617 GenerateGlobalInstanceTypeCheck(masm, ecx, &slow); 618 619 GenerateDictionaryLoad(masm, &slow, ebx, eax, ecx, edi, eax); 620 __ IncrementCounter(counters->keyed_load_generic_symbol(), 1); 621 __ ret(0); 622 623 __ bind(&index_string); 624 __ IndexFromHash(ebx, eax); 625 // Now jump to the place where smi keys are handled. 626 __ jmp(&index_smi); 627} 628 629 630void KeyedLoadIC::GenerateString(MacroAssembler* masm) { 631 // ----------- S t a t e ------------- 632 // -- eax : key (index) 633 // -- edx : receiver 634 // -- esp[0] : return address 635 // ----------------------------------- 636 Label miss; 637 638 Register receiver = edx; 639 Register index = eax; 640 Register scratch = ecx; 641 Register result = eax; 642 643 StringCharAtGenerator char_at_generator(receiver, 644 index, 645 scratch, 646 result, 647 &miss, // When not a string. 648 &miss, // When not a number. 649 &miss, // When index out of range. 650 STRING_INDEX_IS_ARRAY_INDEX); 651 char_at_generator.GenerateFast(masm); 652 __ ret(0); 653 654 StubRuntimeCallHelper call_helper; 655 char_at_generator.GenerateSlow(masm, call_helper); 656 657 __ bind(&miss); 658 GenerateMiss(masm, false); 659} 660 661 662void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { 663 // ----------- S t a t e ------------- 664 // -- eax : key 665 // -- edx : receiver 666 // -- esp[0] : return address 667 // ----------------------------------- 668 Label slow; 669 670 // Check that the receiver isn't a smi. 671 __ JumpIfSmi(edx, &slow); 672 673 // Check that the key is an array index, that is Uint32. 674 __ test(eax, Immediate(kSmiTagMask | kSmiSignMask)); 675 __ j(not_zero, &slow); 676 677 // Get the map of the receiver. 678 __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); 679 680 // Check that it has indexed interceptor and access checks 681 // are not enabled for this object. 682 __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset)); 683 __ and_(ecx, Immediate(kSlowCaseBitFieldMask)); 684 __ cmp(ecx, Immediate(1 << Map::kHasIndexedInterceptor)); 685 __ j(not_zero, &slow); 686 687 // Everything is fine, call runtime. 688 __ pop(ecx); 689 __ push(edx); // receiver 690 __ push(eax); // key 691 __ push(ecx); // return address 692 693 // Perform tail call to the entry. 694 ExternalReference ref = 695 ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor), 696 masm->isolate()); 697 __ TailCallExternalReference(ref, 2, 1); 698 699 __ bind(&slow); 700 GenerateMiss(masm, false); 701} 702 703 704void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) { 705 // ----------- S t a t e ------------- 706 // -- eax : key 707 // -- edx : receiver 708 // -- esp[0] : return address 709 // ----------------------------------- 710 Label slow, notin; 711 Factory* factory = masm->isolate()->factory(); 712 Operand mapped_location = 713 GenerateMappedArgumentsLookup(masm, edx, eax, ebx, ecx, ¬in, &slow); 714 __ mov(eax, mapped_location); 715 __ Ret(); 716 __ bind(¬in); 717 // The unmapped lookup expects that the parameter map is in ebx. 718 Operand unmapped_location = 719 GenerateUnmappedArgumentsLookup(masm, eax, ebx, ecx, &slow); 720 __ cmp(unmapped_location, factory->the_hole_value()); 721 __ j(equal, &slow); 722 __ mov(eax, unmapped_location); 723 __ Ret(); 724 __ bind(&slow); 725 GenerateMiss(masm, false); 726} 727 728 729void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) { 730 // ----------- S t a t e ------------- 731 // -- eax : value 732 // -- ecx : key 733 // -- edx : receiver 734 // -- esp[0] : return address 735 // ----------------------------------- 736 Label slow, notin; 737 Operand mapped_location = 738 GenerateMappedArgumentsLookup(masm, edx, ecx, ebx, edi, ¬in, &slow); 739 __ mov(mapped_location, eax); 740 __ lea(ecx, mapped_location); 741 __ mov(edx, eax); 742 __ RecordWrite(ebx, ecx, edx, kDontSaveFPRegs); 743 __ Ret(); 744 __ bind(¬in); 745 // The unmapped lookup expects that the parameter map is in ebx. 746 Operand unmapped_location = 747 GenerateUnmappedArgumentsLookup(masm, ecx, ebx, edi, &slow); 748 __ mov(unmapped_location, eax); 749 __ lea(edi, unmapped_location); 750 __ mov(edx, eax); 751 __ RecordWrite(ebx, edi, edx, kDontSaveFPRegs); 752 __ Ret(); 753 __ bind(&slow); 754 GenerateMiss(masm, false); 755} 756 757 758void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, 759 StrictModeFlag strict_mode) { 760 // ----------- S t a t e ------------- 761 // -- eax : value 762 // -- ecx : key 763 // -- edx : receiver 764 // -- esp[0] : return address 765 // ----------------------------------- 766 Label slow, fast_object_with_map_check, fast_object_without_map_check; 767 Label fast_double_with_map_check, fast_double_without_map_check; 768 Label check_if_double_array, array, extra, transition_smi_elements; 769 Label finish_object_store, non_double_value, transition_double_elements; 770 771 // Check that the object isn't a smi. 772 __ JumpIfSmi(edx, &slow); 773 // Get the map from the receiver. 774 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); 775 // Check that the receiver does not require access checks. We need 776 // to do this because this generic stub does not perform map checks. 777 __ test_b(FieldOperand(edi, Map::kBitFieldOffset), 778 1 << Map::kIsAccessCheckNeeded); 779 __ j(not_zero, &slow); 780 // Check that the key is a smi. 781 __ JumpIfNotSmi(ecx, &slow); 782 __ CmpInstanceType(edi, JS_ARRAY_TYPE); 783 __ j(equal, &array); 784 // Check that the object is some kind of JSObject. 785 __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE); 786 __ j(below, &slow); 787 788 // Object case: Check key against length in the elements array. 789 // eax: value 790 // edx: JSObject 791 // ecx: key (a smi) 792 // edi: receiver map 793 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 794 // Check array bounds. Both the key and the length of FixedArray are smis. 795 __ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); 796 __ j(below, &fast_object_with_map_check); 797 798 // Slow case: call runtime. 799 __ bind(&slow); 800 GenerateRuntimeSetProperty(masm, strict_mode); 801 802 // Extra capacity case: Check if there is extra capacity to 803 // perform the store and update the length. Used for adding one 804 // element to the array by writing to array[array.length]. 805 __ bind(&extra); 806 // eax: value 807 // edx: receiver, a JSArray 808 // ecx: key, a smi. 809 // ebx: receiver->elements, a FixedArray 810 // edi: receiver map 811 // flags: compare (ecx, edx.length()) 812 // do not leave holes in the array: 813 __ j(not_equal, &slow); 814 __ cmp(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); 815 __ j(above_equal, &slow); 816 __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset)); 817 __ cmp(edi, masm->isolate()->factory()->fixed_array_map()); 818 __ j(not_equal, &check_if_double_array); 819 // Add 1 to receiver->length, and go to common element store code for Objects. 820 __ add(FieldOperand(edx, JSArray::kLengthOffset), 821 Immediate(Smi::FromInt(1))); 822 __ jmp(&fast_object_without_map_check); 823 824 __ bind(&check_if_double_array); 825 __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map()); 826 __ j(not_equal, &slow); 827 // Add 1 to receiver->length, and go to common element store code for doubles. 828 __ add(FieldOperand(edx, JSArray::kLengthOffset), 829 Immediate(Smi::FromInt(1))); 830 __ jmp(&fast_double_without_map_check); 831 832 // Array case: Get the length and the elements array from the JS 833 // array. Check that the array is in fast mode (and writable); if it 834 // is the length is always a smi. 835 __ bind(&array); 836 // eax: value 837 // edx: receiver, a JSArray 838 // ecx: key, a smi. 839 // edi: receiver map 840 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 841 842 // Check the key against the length in the array and fall through to the 843 // common store code. 844 __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis. 845 __ j(above_equal, &extra); 846 847 // Fast case: Do the store, could either Object or double. 848 __ bind(&fast_object_with_map_check); 849 // eax: value 850 // ecx: key (a smi) 851 // edx: receiver 852 // ebx: FixedArray receiver->elements 853 // edi: receiver map 854 __ mov(edi, FieldOperand(ebx, HeapObject::kMapOffset)); 855 __ cmp(edi, masm->isolate()->factory()->fixed_array_map()); 856 __ j(not_equal, &fast_double_with_map_check); 857 __ bind(&fast_object_without_map_check); 858 // Smi stores don't require further checks. 859 Label non_smi_value; 860 __ JumpIfNotSmi(eax, &non_smi_value); 861 // It's irrelevant whether array is smi-only or not when writing a smi. 862 __ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax); 863 __ ret(0); 864 865 __ bind(&non_smi_value); 866 // Escape to elements kind transition case. 867 __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); 868 __ CheckFastObjectElements(edi, &transition_smi_elements); 869 870 // Fast elements array, store the value to the elements backing store. 871 __ bind(&finish_object_store); 872 __ mov(CodeGenerator::FixedArrayElementOperand(ebx, ecx), eax); 873 // Update write barrier for the elements array address. 874 __ mov(edx, eax); // Preserve the value which is returned. 875 __ RecordWriteArray( 876 ebx, edx, ecx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); 877 __ ret(0); 878 879 __ bind(&fast_double_with_map_check); 880 // Check for fast double array case. If this fails, call through to the 881 // runtime. 882 __ cmp(edi, masm->isolate()->factory()->fixed_double_array_map()); 883 __ j(not_equal, &slow); 884 __ bind(&fast_double_without_map_check); 885 // If the value is a number, store it as a double in the FastDoubleElements 886 // array. 887 __ StoreNumberToDoubleElements(eax, ebx, ecx, edx, xmm0, 888 &transition_double_elements, false); 889 __ ret(0); 890 891 __ bind(&transition_smi_elements); 892 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); 893 894 // Transition the array appropriately depending on the value type. 895 __ CheckMap(eax, 896 masm->isolate()->factory()->heap_number_map(), 897 &non_double_value, 898 DONT_DO_SMI_CHECK); 899 900 // Value is a double. Transition FAST_SMI_ONLY_ELEMENTS -> 901 // FAST_DOUBLE_ELEMENTS and complete the store. 902 __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, 903 FAST_DOUBLE_ELEMENTS, 904 ebx, 905 edi, 906 &slow); 907 ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &slow); 908 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 909 __ jmp(&fast_double_without_map_check); 910 911 __ bind(&non_double_value); 912 // Value is not a double, FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS 913 __ LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, 914 FAST_ELEMENTS, 915 ebx, 916 edi, 917 &slow); 918 ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm); 919 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 920 __ jmp(&finish_object_store); 921 922 __ bind(&transition_double_elements); 923 // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a 924 // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and 925 // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS 926 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); 927 __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, 928 FAST_ELEMENTS, 929 ebx, 930 edi, 931 &slow); 932 ElementsTransitionGenerator::GenerateDoubleToObject(masm, &slow); 933 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 934 __ jmp(&finish_object_store); 935} 936 937 938// The generated code does not accept smi keys. 939// The generated code falls through if both probes miss. 940void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm, 941 int argc, 942 Code::Kind kind, 943 Code::ExtraICState extra_state) { 944 // ----------- S t a t e ------------- 945 // -- ecx : name 946 // -- edx : receiver 947 // ----------------------------------- 948 Label number, non_number, non_string, boolean, probe, miss; 949 950 // Probe the stub cache. 951 Code::Flags flags = Code::ComputeFlags(kind, 952 MONOMORPHIC, 953 extra_state, 954 NORMAL, 955 argc); 956 Isolate* isolate = masm->isolate(); 957 isolate->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, eax); 958 959 // If the stub cache probing failed, the receiver might be a value. 960 // For value objects, we use the map of the prototype objects for 961 // the corresponding JSValue for the cache and that is what we need 962 // to probe. 963 // 964 // Check for number. 965 __ JumpIfSmi(edx, &number); 966 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, ebx); 967 __ j(not_equal, &non_number); 968 __ bind(&number); 969 StubCompiler::GenerateLoadGlobalFunctionPrototype( 970 masm, Context::NUMBER_FUNCTION_INDEX, edx); 971 __ jmp(&probe); 972 973 // Check for string. 974 __ bind(&non_number); 975 __ CmpInstanceType(ebx, FIRST_NONSTRING_TYPE); 976 __ j(above_equal, &non_string); 977 StubCompiler::GenerateLoadGlobalFunctionPrototype( 978 masm, Context::STRING_FUNCTION_INDEX, edx); 979 __ jmp(&probe); 980 981 // Check for boolean. 982 __ bind(&non_string); 983 __ cmp(edx, isolate->factory()->true_value()); 984 __ j(equal, &boolean); 985 __ cmp(edx, isolate->factory()->false_value()); 986 __ j(not_equal, &miss); 987 __ bind(&boolean); 988 StubCompiler::GenerateLoadGlobalFunctionPrototype( 989 masm, Context::BOOLEAN_FUNCTION_INDEX, edx); 990 991 // Probe the stub cache for the value object. 992 __ bind(&probe); 993 isolate->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, no_reg); 994 __ bind(&miss); 995} 996 997 998static void GenerateFunctionTailCall(MacroAssembler* masm, 999 int argc, 1000 Label* miss) { 1001 // ----------- S t a t e ------------- 1002 // -- ecx : name 1003 // -- edi : function 1004 // -- esp[0] : return address 1005 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1006 // -- ... 1007 // -- esp[(argc + 1) * 4] : receiver 1008 // ----------------------------------- 1009 1010 // Check that the result is not a smi. 1011 __ JumpIfSmi(edi, miss); 1012 1013 // Check that the value is a JavaScript function, fetching its map into eax. 1014 __ CmpObjectType(edi, JS_FUNCTION_TYPE, eax); 1015 __ j(not_equal, miss); 1016 1017 // Invoke the function. 1018 ParameterCount actual(argc); 1019 __ InvokeFunction(edi, actual, JUMP_FUNCTION, 1020 NullCallWrapper(), CALL_AS_METHOD); 1021} 1022 1023 1024// The generated code falls through if the call should be handled by runtime. 1025void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) { 1026 // ----------- S t a t e ------------- 1027 // -- ecx : name 1028 // -- esp[0] : return address 1029 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1030 // -- ... 1031 // -- esp[(argc + 1) * 4] : receiver 1032 // ----------------------------------- 1033 Label miss; 1034 1035 // Get the receiver of the function from the stack; 1 ~ return address. 1036 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1037 1038 GenerateStringDictionaryReceiverCheck(masm, edx, eax, ebx, &miss); 1039 1040 // eax: elements 1041 // Search the dictionary placing the result in edi. 1042 GenerateDictionaryLoad(masm, &miss, eax, ecx, edi, ebx, edi); 1043 GenerateFunctionTailCall(masm, argc, &miss); 1044 1045 __ bind(&miss); 1046} 1047 1048 1049void CallICBase::GenerateMiss(MacroAssembler* masm, 1050 int argc, 1051 IC::UtilityId id, 1052 Code::ExtraICState extra_state) { 1053 // ----------- S t a t e ------------- 1054 // -- ecx : name 1055 // -- esp[0] : return address 1056 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1057 // -- ... 1058 // -- esp[(argc + 1) * 4] : receiver 1059 // ----------------------------------- 1060 1061 Counters* counters = masm->isolate()->counters(); 1062 if (id == IC::kCallIC_Miss) { 1063 __ IncrementCounter(counters->call_miss(), 1); 1064 } else { 1065 __ IncrementCounter(counters->keyed_call_miss(), 1); 1066 } 1067 1068 // Get the receiver of the function from the stack; 1 ~ return address. 1069 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1070 1071 { 1072 FrameScope scope(masm, StackFrame::INTERNAL); 1073 1074 // Push the receiver and the name of the function. 1075 __ push(edx); 1076 __ push(ecx); 1077 1078 // Call the entry. 1079 CEntryStub stub(1); 1080 __ mov(eax, Immediate(2)); 1081 __ mov(ebx, Immediate(ExternalReference(IC_Utility(id), masm->isolate()))); 1082 __ CallStub(&stub); 1083 1084 // Move result to edi and exit the internal frame. 1085 __ mov(edi, eax); 1086 } 1087 1088 // Check if the receiver is a global object of some sort. 1089 // This can happen only for regular CallIC but not KeyedCallIC. 1090 if (id == IC::kCallIC_Miss) { 1091 Label invoke, global; 1092 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver 1093 __ JumpIfSmi(edx, &invoke, Label::kNear); 1094 __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); 1095 __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); 1096 __ cmp(ebx, JS_GLOBAL_OBJECT_TYPE); 1097 __ j(equal, &global, Label::kNear); 1098 __ cmp(ebx, JS_BUILTINS_OBJECT_TYPE); 1099 __ j(not_equal, &invoke, Label::kNear); 1100 1101 // Patch the receiver on the stack. 1102 __ bind(&global); 1103 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 1104 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); 1105 __ bind(&invoke); 1106 } 1107 1108 // Invoke the function. 1109 CallKind call_kind = CallICBase::Contextual::decode(extra_state) 1110 ? CALL_AS_FUNCTION 1111 : CALL_AS_METHOD; 1112 ParameterCount actual(argc); 1113 __ InvokeFunction(edi, 1114 actual, 1115 JUMP_FUNCTION, 1116 NullCallWrapper(), 1117 call_kind); 1118} 1119 1120 1121void CallIC::GenerateMegamorphic(MacroAssembler* masm, 1122 int argc, 1123 Code::ExtraICState extra_state) { 1124 // ----------- S t a t e ------------- 1125 // -- ecx : name 1126 // -- esp[0] : return address 1127 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1128 // -- ... 1129 // -- esp[(argc + 1) * 4] : receiver 1130 // ----------------------------------- 1131 1132 // Get the receiver of the function from the stack; 1 ~ return address. 1133 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1134 CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, 1135 extra_state); 1136 1137 GenerateMiss(masm, argc, extra_state); 1138} 1139 1140 1141void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { 1142 // ----------- S t a t e ------------- 1143 // -- ecx : name 1144 // -- esp[0] : return address 1145 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1146 // -- ... 1147 // -- esp[(argc + 1) * 4] : receiver 1148 // ----------------------------------- 1149 1150 // Get the receiver of the function from the stack; 1 ~ return address. 1151 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1152 1153 Label do_call, slow_call, slow_load, slow_reload_receiver; 1154 Label check_number_dictionary, check_string, lookup_monomorphic_cache; 1155 Label index_smi, index_string; 1156 1157 // Check that the key is a smi. 1158 __ JumpIfNotSmi(ecx, &check_string); 1159 1160 __ bind(&index_smi); 1161 // Now the key is known to be a smi. This place is also jumped to from 1162 // where a numeric string is converted to a smi. 1163 1164 GenerateKeyedLoadReceiverCheck( 1165 masm, edx, eax, Map::kHasIndexedInterceptor, &slow_call); 1166 1167 GenerateFastArrayLoad( 1168 masm, edx, ecx, eax, edi, &check_number_dictionary, &slow_load); 1169 Isolate* isolate = masm->isolate(); 1170 Counters* counters = isolate->counters(); 1171 __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1); 1172 1173 __ bind(&do_call); 1174 // receiver in edx is not used after this point. 1175 // ecx: key 1176 // edi: function 1177 GenerateFunctionTailCall(masm, argc, &slow_call); 1178 1179 __ bind(&check_number_dictionary); 1180 // eax: elements 1181 // ecx: smi key 1182 // Check whether the elements is a number dictionary. 1183 __ CheckMap(eax, 1184 isolate->factory()->hash_table_map(), 1185 &slow_load, 1186 DONT_DO_SMI_CHECK); 1187 __ mov(ebx, ecx); 1188 __ SmiUntag(ebx); 1189 // ebx: untagged index 1190 // Receiver in edx will be clobbered, need to reload it on miss. 1191 __ LoadFromNumberDictionary( 1192 &slow_reload_receiver, eax, ecx, ebx, edx, edi, edi); 1193 __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1); 1194 __ jmp(&do_call); 1195 1196 __ bind(&slow_reload_receiver); 1197 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1198 1199 __ bind(&slow_load); 1200 // This branch is taken when calling KeyedCallIC_Miss is neither required 1201 // nor beneficial. 1202 __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1); 1203 1204 { 1205 FrameScope scope(masm, StackFrame::INTERNAL); 1206 __ push(ecx); // save the key 1207 __ push(edx); // pass the receiver 1208 __ push(ecx); // pass the key 1209 __ CallRuntime(Runtime::kKeyedGetProperty, 2); 1210 __ pop(ecx); // restore the key 1211 // Leave the internal frame. 1212 } 1213 1214 __ mov(edi, eax); 1215 __ jmp(&do_call); 1216 1217 __ bind(&check_string); 1218 GenerateKeyStringCheck(masm, ecx, eax, ebx, &index_string, &slow_call); 1219 1220 // The key is known to be a symbol. 1221 // If the receiver is a regular JS object with slow properties then do 1222 // a quick inline probe of the receiver's dictionary. 1223 // Otherwise do the monomorphic cache probe. 1224 GenerateKeyedLoadReceiverCheck( 1225 masm, edx, eax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache); 1226 1227 __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset)); 1228 __ CheckMap(ebx, 1229 isolate->factory()->hash_table_map(), 1230 &lookup_monomorphic_cache, 1231 DONT_DO_SMI_CHECK); 1232 1233 GenerateDictionaryLoad(masm, &slow_load, ebx, ecx, eax, edi, edi); 1234 __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1); 1235 __ jmp(&do_call); 1236 1237 __ bind(&lookup_monomorphic_cache); 1238 __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1); 1239 CallICBase::GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, 1240 Code::kNoExtraICState); 1241 // Fall through on miss. 1242 1243 __ bind(&slow_call); 1244 // This branch is taken if: 1245 // - the receiver requires boxing or access check, 1246 // - the key is neither smi nor symbol, 1247 // - the value loaded is not a function, 1248 // - there is hope that the runtime will create a monomorphic call stub 1249 // that will get fetched next time. 1250 __ IncrementCounter(counters->keyed_call_generic_slow(), 1); 1251 GenerateMiss(masm, argc); 1252 1253 __ bind(&index_string); 1254 __ IndexFromHash(ebx, ecx); 1255 // Now jump to the place where smi keys are handled. 1256 __ jmp(&index_smi); 1257} 1258 1259 1260void KeyedCallIC::GenerateNonStrictArguments(MacroAssembler* masm, 1261 int argc) { 1262 // ----------- S t a t e ------------- 1263 // -- ecx : name 1264 // -- esp[0] : return address 1265 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1266 // -- ... 1267 // -- esp[(argc + 1) * 4] : receiver 1268 // ----------------------------------- 1269 Label slow, notin; 1270 Factory* factory = masm->isolate()->factory(); 1271 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1272 Operand mapped_location = 1273 GenerateMappedArgumentsLookup(masm, edx, ecx, ebx, eax, ¬in, &slow); 1274 __ mov(edi, mapped_location); 1275 GenerateFunctionTailCall(masm, argc, &slow); 1276 __ bind(¬in); 1277 // The unmapped lookup expects that the parameter map is in ebx. 1278 Operand unmapped_location = 1279 GenerateUnmappedArgumentsLookup(masm, ecx, ebx, eax, &slow); 1280 __ cmp(unmapped_location, factory->the_hole_value()); 1281 __ j(equal, &slow); 1282 __ mov(edi, unmapped_location); 1283 GenerateFunctionTailCall(masm, argc, &slow); 1284 __ bind(&slow); 1285 GenerateMiss(masm, argc); 1286} 1287 1288 1289void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) { 1290 // ----------- S t a t e ------------- 1291 // -- ecx : name 1292 // -- esp[0] : return address 1293 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1294 // -- ... 1295 // -- esp[(argc + 1) * 4] : receiver 1296 // ----------------------------------- 1297 1298 // Check if the name is a string. 1299 Label miss; 1300 __ JumpIfSmi(ecx, &miss); 1301 Condition cond = masm->IsObjectStringType(ecx, eax, eax); 1302 __ j(NegateCondition(cond), &miss); 1303 CallICBase::GenerateNormal(masm, argc); 1304 __ bind(&miss); 1305 GenerateMiss(masm, argc); 1306} 1307 1308 1309void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { 1310 // ----------- S t a t e ------------- 1311 // -- eax : receiver 1312 // -- ecx : name 1313 // -- esp[0] : return address 1314 // ----------------------------------- 1315 1316 // Probe the stub cache. 1317 Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC); 1318 Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, eax, ecx, ebx, 1319 edx); 1320 1321 // Cache miss: Jump to runtime. 1322 GenerateMiss(masm); 1323} 1324 1325 1326void LoadIC::GenerateNormal(MacroAssembler* masm) { 1327 // ----------- S t a t e ------------- 1328 // -- eax : receiver 1329 // -- ecx : name 1330 // -- esp[0] : return address 1331 // ----------------------------------- 1332 Label miss; 1333 1334 GenerateStringDictionaryReceiverCheck(masm, eax, edx, ebx, &miss); 1335 1336 // edx: elements 1337 // Search the dictionary placing the result in eax. 1338 GenerateDictionaryLoad(masm, &miss, edx, ecx, edi, ebx, eax); 1339 __ ret(0); 1340 1341 // Cache miss: Jump to runtime. 1342 __ bind(&miss); 1343 GenerateMiss(masm); 1344} 1345 1346 1347void LoadIC::GenerateMiss(MacroAssembler* masm) { 1348 // ----------- S t a t e ------------- 1349 // -- eax : receiver 1350 // -- ecx : name 1351 // -- esp[0] : return address 1352 // ----------------------------------- 1353 1354 __ IncrementCounter(masm->isolate()->counters()->load_miss(), 1); 1355 1356 __ pop(ebx); 1357 __ push(eax); // receiver 1358 __ push(ecx); // name 1359 __ push(ebx); // return address 1360 1361 // Perform tail call to the entry. 1362 ExternalReference ref = 1363 ExternalReference(IC_Utility(kLoadIC_Miss), masm->isolate()); 1364 __ TailCallExternalReference(ref, 2, 1); 1365} 1366 1367 1368void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) { 1369 // ----------- S t a t e ------------- 1370 // -- eax : key 1371 // -- edx : receiver 1372 // -- esp[0] : return address 1373 // ----------------------------------- 1374 1375 __ IncrementCounter(masm->isolate()->counters()->keyed_load_miss(), 1); 1376 1377 __ pop(ebx); 1378 __ push(edx); // receiver 1379 __ push(eax); // name 1380 __ push(ebx); // return address 1381 1382 // Perform tail call to the entry. 1383 ExternalReference ref = force_generic 1384 ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), 1385 masm->isolate()) 1386 : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), masm->isolate()); 1387 __ TailCallExternalReference(ref, 2, 1); 1388} 1389 1390 1391void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { 1392 // ----------- S t a t e ------------- 1393 // -- eax : key 1394 // -- edx : receiver 1395 // -- esp[0] : return address 1396 // ----------------------------------- 1397 1398 __ pop(ebx); 1399 __ push(edx); // receiver 1400 __ push(eax); // name 1401 __ push(ebx); // return address 1402 1403 // Perform tail call to the entry. 1404 __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1); 1405} 1406 1407 1408void StoreIC::GenerateMegamorphic(MacroAssembler* masm, 1409 StrictModeFlag strict_mode) { 1410 // ----------- S t a t e ------------- 1411 // -- eax : value 1412 // -- ecx : name 1413 // -- edx : receiver 1414 // -- esp[0] : return address 1415 // ----------------------------------- 1416 1417 Code::Flags flags = 1418 Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode); 1419 Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx, 1420 no_reg); 1421 1422 // Cache miss: Jump to runtime. 1423 GenerateMiss(masm); 1424} 1425 1426 1427void StoreIC::GenerateMiss(MacroAssembler* masm) { 1428 // ----------- S t a t e ------------- 1429 // -- eax : value 1430 // -- ecx : name 1431 // -- edx : receiver 1432 // -- esp[0] : return address 1433 // ----------------------------------- 1434 1435 __ pop(ebx); 1436 __ push(edx); 1437 __ push(ecx); 1438 __ push(eax); 1439 __ push(ebx); 1440 1441 // Perform tail call to the entry. 1442 ExternalReference ref = 1443 ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate()); 1444 __ TailCallExternalReference(ref, 3, 1); 1445} 1446 1447 1448void StoreIC::GenerateArrayLength(MacroAssembler* masm) { 1449 // ----------- S t a t e ------------- 1450 // -- eax : value 1451 // -- ecx : name 1452 // -- edx : receiver 1453 // -- esp[0] : return address 1454 // ----------------------------------- 1455 // 1456 // This accepts as a receiver anything JSArray::SetElementsLength accepts 1457 // (currently anything except for external arrays which means anything with 1458 // elements of FixedArray type). Value must be a number, but only smis are 1459 // accepted as the most common case. 1460 1461 Label miss; 1462 1463 Register receiver = edx; 1464 Register value = eax; 1465 Register scratch = ebx; 1466 1467 // Check that the receiver isn't a smi. 1468 __ JumpIfSmi(receiver, &miss); 1469 1470 // Check that the object is a JS array. 1471 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch); 1472 __ j(not_equal, &miss); 1473 1474 // Check that elements are FixedArray. 1475 // We rely on StoreIC_ArrayLength below to deal with all types of 1476 // fast elements (including COW). 1477 __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset)); 1478 __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch); 1479 __ j(not_equal, &miss); 1480 1481 // Check that the array has fast properties, otherwise the length 1482 // property might have been redefined. 1483 __ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset)); 1484 __ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset), 1485 Heap::kHashTableMapRootIndex); 1486 __ j(equal, &miss); 1487 1488 // Check that value is a smi. 1489 __ JumpIfNotSmi(value, &miss); 1490 1491 // Prepare tail call to StoreIC_ArrayLength. 1492 __ pop(scratch); 1493 __ push(receiver); 1494 __ push(value); 1495 __ push(scratch); // return address 1496 1497 ExternalReference ref = 1498 ExternalReference(IC_Utility(kStoreIC_ArrayLength), masm->isolate()); 1499 __ TailCallExternalReference(ref, 2, 1); 1500 1501 __ bind(&miss); 1502 1503 GenerateMiss(masm); 1504} 1505 1506 1507void StoreIC::GenerateNormal(MacroAssembler* masm) { 1508 // ----------- S t a t e ------------- 1509 // -- eax : value 1510 // -- ecx : name 1511 // -- edx : receiver 1512 // -- esp[0] : return address 1513 // ----------------------------------- 1514 1515 Label miss, restore_miss; 1516 1517 GenerateStringDictionaryReceiverCheck(masm, edx, ebx, edi, &miss); 1518 1519 // A lot of registers are needed for storing to slow case 1520 // objects. Push and restore receiver but rely on 1521 // GenerateDictionaryStore preserving the value and name. 1522 __ push(edx); 1523 GenerateDictionaryStore(masm, &restore_miss, ebx, ecx, eax, edx, edi); 1524 __ Drop(1); 1525 Counters* counters = masm->isolate()->counters(); 1526 __ IncrementCounter(counters->store_normal_hit(), 1); 1527 __ ret(0); 1528 1529 __ bind(&restore_miss); 1530 __ pop(edx); 1531 1532 __ bind(&miss); 1533 __ IncrementCounter(counters->store_normal_miss(), 1); 1534 GenerateMiss(masm); 1535} 1536 1537 1538void StoreIC::GenerateGlobalProxy(MacroAssembler* masm, 1539 StrictModeFlag strict_mode) { 1540 // ----------- S t a t e ------------- 1541 // -- eax : value 1542 // -- ecx : name 1543 // -- edx : receiver 1544 // -- esp[0] : return address 1545 // ----------------------------------- 1546 __ pop(ebx); 1547 __ push(edx); 1548 __ push(ecx); 1549 __ push(eax); 1550 __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes 1551 __ push(Immediate(Smi::FromInt(strict_mode))); 1552 __ push(ebx); // return address 1553 1554 // Do tail-call to runtime routine. 1555 __ TailCallRuntime(Runtime::kSetProperty, 5, 1); 1556} 1557 1558 1559void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, 1560 StrictModeFlag strict_mode) { 1561 // ----------- S t a t e ------------- 1562 // -- eax : value 1563 // -- ecx : key 1564 // -- edx : receiver 1565 // -- esp[0] : return address 1566 // ----------------------------------- 1567 1568 __ pop(ebx); 1569 __ push(edx); 1570 __ push(ecx); 1571 __ push(eax); 1572 __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes 1573 __ push(Immediate(Smi::FromInt(strict_mode))); // Strict mode. 1574 __ push(ebx); // return address 1575 1576 // Do tail-call to runtime routine. 1577 __ TailCallRuntime(Runtime::kSetProperty, 5, 1); 1578} 1579 1580 1581void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) { 1582 // ----------- S t a t e ------------- 1583 // -- eax : value 1584 // -- ecx : key 1585 // -- edx : receiver 1586 // -- esp[0] : return address 1587 // ----------------------------------- 1588 1589 __ pop(ebx); 1590 __ push(edx); 1591 __ push(ecx); 1592 __ push(eax); 1593 __ push(ebx); 1594 1595 // Do tail-call to runtime routine. 1596 ExternalReference ref = force_generic 1597 ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric), 1598 masm->isolate()) 1599 : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate()); 1600 __ TailCallExternalReference(ref, 3, 1); 1601} 1602 1603 1604void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) { 1605 // ----------- S t a t e ------------- 1606 // -- eax : value 1607 // -- ecx : key 1608 // -- edx : receiver 1609 // -- esp[0] : return address 1610 // ----------------------------------- 1611 1612 __ pop(ebx); 1613 __ push(edx); 1614 __ push(ecx); 1615 __ push(eax); 1616 __ push(ebx); // return address 1617 1618 // Do tail-call to runtime routine. 1619 ExternalReference ref(IC_Utility(kKeyedStoreIC_Slow), masm->isolate()); 1620 __ TailCallExternalReference(ref, 3, 1); 1621} 1622 1623 1624void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) { 1625 // ----------- S t a t e ------------- 1626 // -- ebx : target map 1627 // -- edx : receiver 1628 // -- esp[0] : return address 1629 // ----------------------------------- 1630 // Must return the modified receiver in eax. 1631 if (!FLAG_trace_elements_transitions) { 1632 Label fail; 1633 ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail); 1634 __ mov(eax, edx); 1635 __ Ret(); 1636 __ bind(&fail); 1637 } 1638 1639 __ pop(ebx); 1640 __ push(edx); 1641 __ push(ebx); // return address 1642 // Leaving the code managed by the register allocator and return to the 1643 // convention of using esi as context register. 1644 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1645 __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1); 1646} 1647 1648 1649void KeyedStoreIC::GenerateTransitionElementsDoubleToObject( 1650 MacroAssembler* masm) { 1651 // ----------- S t a t e ------------- 1652 // -- ebx : target map 1653 // -- edx : receiver 1654 // -- esp[0] : return address 1655 // ----------------------------------- 1656 // Must return the modified receiver in eax. 1657 if (!FLAG_trace_elements_transitions) { 1658 Label fail; 1659 ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail); 1660 __ mov(eax, edx); 1661 __ Ret(); 1662 __ bind(&fail); 1663 } 1664 1665 __ pop(ebx); 1666 __ push(edx); 1667 __ push(ebx); // return address 1668 // Leaving the code managed by the register allocator and return to the 1669 // convention of using esi as context register. 1670 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1671 __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1); 1672} 1673 1674 1675#undef __ 1676 1677 1678Condition CompareIC::ComputeCondition(Token::Value op) { 1679 switch (op) { 1680 case Token::EQ_STRICT: 1681 case Token::EQ: 1682 return equal; 1683 case Token::LT: 1684 return less; 1685 case Token::GT: 1686 return greater; 1687 case Token::LTE: 1688 return less_equal; 1689 case Token::GTE: 1690 return greater_equal; 1691 default: 1692 UNREACHABLE(); 1693 return no_condition; 1694 } 1695} 1696 1697 1698static bool HasInlinedSmiCode(Address address) { 1699 // The address of the instruction following the call. 1700 Address test_instruction_address = 1701 address + Assembler::kCallTargetAddressOffset; 1702 1703 // If the instruction following the call is not a test al, nothing 1704 // was inlined. 1705 return *test_instruction_address == Assembler::kTestAlByte; 1706} 1707 1708 1709void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) { 1710 HandleScope scope; 1711 Handle<Code> rewritten; 1712 State previous_state = GetState(); 1713 1714 State state = TargetState(previous_state, HasInlinedSmiCode(address()), x, y); 1715 if (state == GENERIC) { 1716 CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS); 1717 rewritten = stub.GetCode(); 1718 } else { 1719 ICCompareStub stub(op_, state); 1720 if (state == KNOWN_OBJECTS) { 1721 stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map())); 1722 } 1723 rewritten = stub.GetCode(); 1724 } 1725 set_target(*rewritten); 1726 1727#ifdef DEBUG 1728 if (FLAG_trace_ic) { 1729 PrintF("[CompareIC (%s->%s)#%s]\n", 1730 GetStateName(previous_state), 1731 GetStateName(state), 1732 Token::Name(op_)); 1733 } 1734#endif 1735 1736 // Activate inlined smi code. 1737 if (previous_state == UNINITIALIZED) { 1738 PatchInlinedSmiCode(address()); 1739 } 1740} 1741 1742 1743void PatchInlinedSmiCode(Address address) { 1744 // The address of the instruction following the call. 1745 Address test_instruction_address = 1746 address + Assembler::kCallTargetAddressOffset; 1747 1748 // If the instruction following the call is not a test al, nothing 1749 // was inlined. 1750 if (*test_instruction_address != Assembler::kTestAlByte) { 1751 ASSERT(*test_instruction_address == Assembler::kNopByte); 1752 return; 1753 } 1754 1755 Address delta_address = test_instruction_address + 1; 1756 // The delta to the start of the map check instruction and the 1757 // condition code uses at the patched jump. 1758 int8_t delta = *reinterpret_cast<int8_t*>(delta_address); 1759 if (FLAG_trace_ic) { 1760 PrintF("[ patching ic at %p, test=%p, delta=%d\n", 1761 address, test_instruction_address, delta); 1762 } 1763 1764 // Patch with a short conditional jump. There must be a 1765 // short jump-if-carry/not-carry at this position. 1766 Address jmp_address = test_instruction_address - delta; 1767 ASSERT(*jmp_address == Assembler::kJncShortOpcode || 1768 *jmp_address == Assembler::kJcShortOpcode); 1769 Condition cc = *jmp_address == Assembler::kJncShortOpcode 1770 ? not_zero 1771 : zero; 1772 *jmp_address = static_cast<byte>(Assembler::kJccShortPrefix | cc); 1773} 1774 1775 1776} } // namespace v8::internal 1777 1778#endif // V8_TARGET_ARCH_IA32 1779