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