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