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_MIPS 31 32 #include "ic-inl.h" 33 #include "codegen.h" 34 #include "stub-cache.h" 35 36 namespace v8 { 37 namespace internal { 38 39 #define __ ACCESS_MASM(masm) 40 41 42 static void ProbeTable(Isolate* isolate, 43 MacroAssembler* masm, 44 Code::Flags flags, 45 StubCache::Table table, 46 Register receiver, 47 Register name, 48 // Number of the cache entry, not scaled. 49 Register offset, 50 Register scratch, 51 Register scratch2, 52 Register offset_scratch) { 53 ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); 54 ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); 55 ExternalReference map_offset(isolate->stub_cache()->map_reference(table)); 56 57 uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address()); 58 uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address()); 59 uint32_t map_off_addr = reinterpret_cast<uint32_t>(map_offset.address()); 60 61 // Check the relative positions of the address fields. 62 ASSERT(value_off_addr > key_off_addr); 63 ASSERT((value_off_addr - key_off_addr) % 4 == 0); 64 ASSERT((value_off_addr - key_off_addr) < (256 * 4)); 65 ASSERT(map_off_addr > key_off_addr); 66 ASSERT((map_off_addr - key_off_addr) % 4 == 0); 67 ASSERT((map_off_addr - key_off_addr) < (256 * 4)); 68 69 Label miss; 70 Register base_addr = scratch; 71 scratch = no_reg; 72 73 // Multiply by 3 because there are 3 fields per entry (name, code, map). 74 __ sll(offset_scratch, offset, 1); 75 __ Addu(offset_scratch, offset_scratch, offset); 76 77 // Calculate the base address of the entry. 78 __ li(base_addr, Operand(key_offset)); 79 __ sll(at, offset_scratch, kPointerSizeLog2); 80 __ Addu(base_addr, base_addr, at); 81 82 // Check that the key in the entry matches the name. 83 __ lw(at, MemOperand(base_addr, 0)); 84 __ Branch(&miss, ne, name, Operand(at)); 85 86 // Check the map matches. 87 __ lw(at, MemOperand(base_addr, map_off_addr - key_off_addr)); 88 __ lw(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset)); 89 __ Branch(&miss, ne, at, Operand(scratch2)); 90 91 // Get the code entry from the cache. 92 Register code = scratch2; 93 scratch2 = no_reg; 94 __ lw(code, MemOperand(base_addr, value_off_addr - key_off_addr)); 95 96 // Check that the flags match what we're looking for. 97 Register flags_reg = base_addr; 98 base_addr = no_reg; 99 __ lw(flags_reg, FieldMemOperand(code, Code::kFlagsOffset)); 100 __ And(flags_reg, flags_reg, Operand(~Code::kFlagsNotUsedInLookup)); 101 __ Branch(&miss, ne, flags_reg, Operand(flags)); 102 103 #ifdef DEBUG 104 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { 105 __ jmp(&miss); 106 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { 107 __ jmp(&miss); 108 } 109 #endif 110 111 // Jump to the first instruction in the code stub. 112 __ Addu(at, code, Operand(Code::kHeaderSize - kHeapObjectTag)); 113 __ Jump(at); 114 115 // Miss: fall through. 116 __ bind(&miss); 117 } 118 119 120 void StubCompiler::GenerateDictionaryNegativeLookup(MacroAssembler* masm, 121 Label* miss_label, 122 Register receiver, 123 Handle<Name> name, 124 Register scratch0, 125 Register scratch1) { 126 ASSERT(name->IsUniqueName()); 127 ASSERT(!receiver.is(scratch0)); 128 Counters* counters = masm->isolate()->counters(); 129 __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); 130 __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); 131 132 Label done; 133 134 const int kInterceptorOrAccessCheckNeededMask = 135 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 136 137 // Bail out if the receiver has a named interceptor or requires access checks. 138 Register map = scratch1; 139 __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); 140 __ lbu(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); 141 __ And(scratch0, scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); 142 __ Branch(miss_label, ne, scratch0, Operand(zero_reg)); 143 144 // Check that receiver is a JSObject. 145 __ lbu(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); 146 __ Branch(miss_label, lt, scratch0, Operand(FIRST_SPEC_OBJECT_TYPE)); 147 148 // Load properties array. 149 Register properties = scratch0; 150 __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 151 // Check that the properties array is a dictionary. 152 __ lw(map, FieldMemOperand(properties, HeapObject::kMapOffset)); 153 Register tmp = properties; 154 __ LoadRoot(tmp, Heap::kHashTableMapRootIndex); 155 __ Branch(miss_label, ne, map, Operand(tmp)); 156 157 // Restore the temporarily used register. 158 __ lw(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 159 160 161 NameDictionaryLookupStub::GenerateNegativeLookup(masm, 162 miss_label, 163 &done, 164 receiver, 165 properties, 166 name, 167 scratch1); 168 __ bind(&done); 169 __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); 170 } 171 172 173 void StubCache::GenerateProbe(MacroAssembler* masm, 174 Code::Flags flags, 175 Register receiver, 176 Register name, 177 Register scratch, 178 Register extra, 179 Register extra2, 180 Register extra3) { 181 Isolate* isolate = masm->isolate(); 182 Label miss; 183 184 // Make sure that code is valid. The multiplying code relies on the 185 // entry size being 12. 186 ASSERT(sizeof(Entry) == 12); 187 188 // Make sure the flags does not name a specific type. 189 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); 190 191 // Make sure that there are no register conflicts. 192 ASSERT(!scratch.is(receiver)); 193 ASSERT(!scratch.is(name)); 194 ASSERT(!extra.is(receiver)); 195 ASSERT(!extra.is(name)); 196 ASSERT(!extra.is(scratch)); 197 ASSERT(!extra2.is(receiver)); 198 ASSERT(!extra2.is(name)); 199 ASSERT(!extra2.is(scratch)); 200 ASSERT(!extra2.is(extra)); 201 202 // Check register validity. 203 ASSERT(!scratch.is(no_reg)); 204 ASSERT(!extra.is(no_reg)); 205 ASSERT(!extra2.is(no_reg)); 206 ASSERT(!extra3.is(no_reg)); 207 208 Counters* counters = masm->isolate()->counters(); 209 __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1, 210 extra2, extra3); 211 212 // Check that the receiver isn't a smi. 213 __ JumpIfSmi(receiver, &miss); 214 215 // Get the map of the receiver and compute the hash. 216 __ lw(scratch, FieldMemOperand(name, Name::kHashFieldOffset)); 217 __ lw(at, FieldMemOperand(receiver, HeapObject::kMapOffset)); 218 __ Addu(scratch, scratch, at); 219 uint32_t mask = kPrimaryTableSize - 1; 220 // We shift out the last two bits because they are not part of the hash and 221 // they are always 01 for maps. 222 __ srl(scratch, scratch, kHeapObjectTagSize); 223 __ Xor(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask)); 224 __ And(scratch, scratch, Operand(mask)); 225 226 // Probe the primary table. 227 ProbeTable(isolate, 228 masm, 229 flags, 230 kPrimary, 231 receiver, 232 name, 233 scratch, 234 extra, 235 extra2, 236 extra3); 237 238 // Primary miss: Compute hash for secondary probe. 239 __ srl(at, name, kHeapObjectTagSize); 240 __ Subu(scratch, scratch, at); 241 uint32_t mask2 = kSecondaryTableSize - 1; 242 __ Addu(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2)); 243 __ And(scratch, scratch, Operand(mask2)); 244 245 // Probe the secondary table. 246 ProbeTable(isolate, 247 masm, 248 flags, 249 kSecondary, 250 receiver, 251 name, 252 scratch, 253 extra, 254 extra2, 255 extra3); 256 257 // Cache miss: Fall-through and let caller handle the miss by 258 // entering the runtime system. 259 __ bind(&miss); 260 __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1, 261 extra2, extra3); 262 } 263 264 265 void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, 266 int index, 267 Register prototype) { 268 // Load the global or builtins object from the current context. 269 __ lw(prototype, 270 MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); 271 // Load the native context from the global or builtins object. 272 __ lw(prototype, 273 FieldMemOperand(prototype, GlobalObject::kNativeContextOffset)); 274 // Load the function from the native context. 275 __ lw(prototype, MemOperand(prototype, Context::SlotOffset(index))); 276 // Load the initial map. The global functions all have initial maps. 277 __ lw(prototype, 278 FieldMemOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); 279 // Load the prototype from the initial map. 280 __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); 281 } 282 283 284 void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( 285 MacroAssembler* masm, 286 int index, 287 Register prototype, 288 Label* miss) { 289 Isolate* isolate = masm->isolate(); 290 // Check we're still in the same context. 291 __ lw(prototype, 292 MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); 293 ASSERT(!prototype.is(at)); 294 __ li(at, isolate->global_object()); 295 __ Branch(miss, ne, prototype, Operand(at)); 296 // Get the global function with the given index. 297 Handle<JSFunction> function( 298 JSFunction::cast(isolate->native_context()->get(index))); 299 // Load its initial map. The global functions all have initial maps. 300 __ li(prototype, Handle<Map>(function->initial_map())); 301 // Load the prototype from the initial map. 302 __ lw(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset)); 303 } 304 305 306 void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, 307 Register dst, 308 Register src, 309 bool inobject, 310 int index, 311 Representation representation) { 312 ASSERT(!FLAG_track_double_fields || !representation.IsDouble()); 313 int offset = index * kPointerSize; 314 if (!inobject) { 315 // Calculate the offset into the properties array. 316 offset = offset + FixedArray::kHeaderSize; 317 __ lw(dst, FieldMemOperand(src, JSObject::kPropertiesOffset)); 318 src = dst; 319 } 320 __ lw(dst, FieldMemOperand(src, offset)); 321 } 322 323 324 void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, 325 Register receiver, 326 Register scratch, 327 Label* miss_label) { 328 // Check that the receiver isn't a smi. 329 __ JumpIfSmi(receiver, miss_label); 330 331 // Check that the object is a JS array. 332 __ GetObjectType(receiver, scratch, scratch); 333 __ Branch(miss_label, ne, scratch, Operand(JS_ARRAY_TYPE)); 334 335 // Load length directly from the JS array. 336 __ Ret(USE_DELAY_SLOT); 337 __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 338 } 339 340 341 // Generate code to check if an object is a string. If the object is a 342 // heap object, its map's instance type is left in the scratch1 register. 343 // If this is not needed, scratch1 and scratch2 may be the same register. 344 static void GenerateStringCheck(MacroAssembler* masm, 345 Register receiver, 346 Register scratch1, 347 Register scratch2, 348 Label* smi, 349 Label* non_string_object) { 350 // Check that the receiver isn't a smi. 351 __ JumpIfSmi(receiver, smi, t0); 352 353 // Check that the object is a string. 354 __ lw(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset)); 355 __ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); 356 __ And(scratch2, scratch1, Operand(kIsNotStringMask)); 357 // The cast is to resolve the overload for the argument of 0x0. 358 __ Branch(non_string_object, 359 ne, 360 scratch2, 361 Operand(static_cast<int32_t>(kStringTag))); 362 } 363 364 365 // Generate code to load the length from a string object and return the length. 366 // If the receiver object is not a string or a wrapped string object the 367 // execution continues at the miss label. The register containing the 368 // receiver is potentially clobbered. 369 void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, 370 Register receiver, 371 Register scratch1, 372 Register scratch2, 373 Label* miss) { 374 Label check_wrapper; 375 376 // Check if the object is a string leaving the instance type in the 377 // scratch1 register. 378 GenerateStringCheck(masm, receiver, scratch1, scratch2, miss, &check_wrapper); 379 380 // Load length directly from the string. 381 __ Ret(USE_DELAY_SLOT); 382 __ lw(v0, FieldMemOperand(receiver, String::kLengthOffset)); 383 384 // Check if the object is a JSValue wrapper. 385 __ bind(&check_wrapper); 386 __ Branch(miss, ne, scratch1, Operand(JS_VALUE_TYPE)); 387 388 // Unwrap the value and check if the wrapped value is a string. 389 __ lw(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset)); 390 GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss); 391 __ Ret(USE_DELAY_SLOT); 392 __ lw(v0, FieldMemOperand(scratch1, String::kLengthOffset)); 393 } 394 395 396 void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, 397 Register receiver, 398 Register scratch1, 399 Register scratch2, 400 Label* miss_label) { 401 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); 402 __ Ret(USE_DELAY_SLOT); 403 __ mov(v0, scratch1); 404 } 405 406 407 void StubCompiler::GenerateCheckPropertyCell(MacroAssembler* masm, 408 Handle<JSGlobalObject> global, 409 Handle<Name> name, 410 Register scratch, 411 Label* miss) { 412 Handle<Cell> cell = JSGlobalObject::EnsurePropertyCell(global, name); 413 ASSERT(cell->value()->IsTheHole()); 414 __ li(scratch, Operand(cell)); 415 __ lw(scratch, FieldMemOperand(scratch, Cell::kValueOffset)); 416 __ LoadRoot(at, Heap::kTheHoleValueRootIndex); 417 __ Branch(miss, ne, scratch, Operand(at)); 418 } 419 420 421 void StoreStubCompiler::GenerateNegativeHolderLookup( 422 MacroAssembler* masm, 423 Handle<JSObject> holder, 424 Register holder_reg, 425 Handle<Name> name, 426 Label* miss) { 427 if (holder->IsJSGlobalObject()) { 428 GenerateCheckPropertyCell( 429 masm, Handle<JSGlobalObject>::cast(holder), name, scratch1(), miss); 430 } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) { 431 GenerateDictionaryNegativeLookup( 432 masm, miss, holder_reg, name, scratch1(), scratch2()); 433 } 434 } 435 436 437 // Generate StoreTransition code, value is passed in a0 register. 438 // After executing generated code, the receiver_reg and name_reg 439 // may be clobbered. 440 void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm, 441 Handle<JSObject> object, 442 LookupResult* lookup, 443 Handle<Map> transition, 444 Handle<Name> name, 445 Register receiver_reg, 446 Register storage_reg, 447 Register value_reg, 448 Register scratch1, 449 Register scratch2, 450 Register scratch3, 451 Label* miss_label, 452 Label* slow) { 453 // a0 : value. 454 Label exit; 455 456 int descriptor = transition->LastAdded(); 457 DescriptorArray* descriptors = transition->instance_descriptors(); 458 PropertyDetails details = descriptors->GetDetails(descriptor); 459 Representation representation = details.representation(); 460 ASSERT(!representation.IsNone()); 461 462 if (details.type() == CONSTANT) { 463 Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate()); 464 __ li(scratch1, constant); 465 __ Branch(miss_label, ne, value_reg, Operand(scratch1)); 466 } else if (FLAG_track_fields && representation.IsSmi()) { 467 __ JumpIfNotSmi(value_reg, miss_label); 468 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { 469 __ JumpIfSmi(value_reg, miss_label); 470 } else if (FLAG_track_double_fields && representation.IsDouble()) { 471 Label do_store, heap_number; 472 __ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex); 473 __ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow); 474 475 __ JumpIfNotSmi(value_reg, &heap_number); 476 __ SmiUntag(scratch1, value_reg); 477 __ mtc1(scratch1, f6); 478 __ cvt_d_w(f4, f6); 479 __ jmp(&do_store); 480 481 __ bind(&heap_number); 482 __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex, 483 miss_label, DONT_DO_SMI_CHECK); 484 __ ldc1(f4, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); 485 486 __ bind(&do_store); 487 __ sdc1(f4, FieldMemOperand(storage_reg, HeapNumber::kValueOffset)); 488 } 489 490 // Stub never generated for non-global objects that require access 491 // checks. 492 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 493 494 // Perform map transition for the receiver if necessary. 495 if (details.type() == FIELD && 496 object->map()->unused_property_fields() == 0) { 497 // The properties must be extended before we can store the value. 498 // We jump to a runtime call that extends the properties array. 499 __ push(receiver_reg); 500 __ li(a2, Operand(transition)); 501 __ Push(a2, a0); 502 __ TailCallExternalReference( 503 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), 504 masm->isolate()), 505 3, 1); 506 return; 507 } 508 509 // Update the map of the object. 510 __ li(scratch1, Operand(transition)); 511 __ sw(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset)); 512 513 // Update the write barrier for the map field. 514 __ RecordWriteField(receiver_reg, 515 HeapObject::kMapOffset, 516 scratch1, 517 scratch2, 518 kRAHasNotBeenSaved, 519 kDontSaveFPRegs, 520 OMIT_REMEMBERED_SET, 521 OMIT_SMI_CHECK); 522 523 if (details.type() == CONSTANT) { 524 ASSERT(value_reg.is(a0)); 525 __ Ret(USE_DELAY_SLOT); 526 __ mov(v0, a0); 527 return; 528 } 529 530 int index = transition->instance_descriptors()->GetFieldIndex( 531 transition->LastAdded()); 532 533 // Adjust for the number of properties stored in the object. Even in the 534 // face of a transition we can use the old map here because the size of the 535 // object and the number of in-object properties is not going to change. 536 index -= object->map()->inobject_properties(); 537 538 // TODO(verwaest): Share this code as a code stub. 539 SmiCheck smi_check = representation.IsTagged() 540 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 541 if (index < 0) { 542 // Set the property straight into the object. 543 int offset = object->map()->instance_size() + (index * kPointerSize); 544 if (FLAG_track_double_fields && representation.IsDouble()) { 545 __ sw(storage_reg, FieldMemOperand(receiver_reg, offset)); 546 } else { 547 __ sw(value_reg, FieldMemOperand(receiver_reg, offset)); 548 } 549 550 if (!FLAG_track_fields || !representation.IsSmi()) { 551 // Update the write barrier for the array address. 552 if (!FLAG_track_double_fields || !representation.IsDouble()) { 553 __ mov(storage_reg, value_reg); 554 } 555 __ RecordWriteField(receiver_reg, 556 offset, 557 storage_reg, 558 scratch1, 559 kRAHasNotBeenSaved, 560 kDontSaveFPRegs, 561 EMIT_REMEMBERED_SET, 562 smi_check); 563 } 564 } else { 565 // Write to the properties array. 566 int offset = index * kPointerSize + FixedArray::kHeaderSize; 567 // Get the properties array 568 __ lw(scratch1, 569 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); 570 if (FLAG_track_double_fields && representation.IsDouble()) { 571 __ sw(storage_reg, FieldMemOperand(scratch1, offset)); 572 } else { 573 __ sw(value_reg, FieldMemOperand(scratch1, offset)); 574 } 575 576 if (!FLAG_track_fields || !representation.IsSmi()) { 577 // Update the write barrier for the array address. 578 if (!FLAG_track_double_fields || !representation.IsDouble()) { 579 __ mov(storage_reg, value_reg); 580 } 581 __ RecordWriteField(scratch1, 582 offset, 583 storage_reg, 584 receiver_reg, 585 kRAHasNotBeenSaved, 586 kDontSaveFPRegs, 587 EMIT_REMEMBERED_SET, 588 smi_check); 589 } 590 } 591 592 // Return the value (register v0). 593 ASSERT(value_reg.is(a0)); 594 __ bind(&exit); 595 __ Ret(USE_DELAY_SLOT); 596 __ mov(v0, a0); 597 } 598 599 600 // Generate StoreField code, value is passed in a0 register. 601 // When leaving generated code after success, the receiver_reg and name_reg 602 // may be clobbered. Upon branch to miss_label, the receiver and name 603 // registers have their original values. 604 void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, 605 Handle<JSObject> object, 606 LookupResult* lookup, 607 Register receiver_reg, 608 Register name_reg, 609 Register value_reg, 610 Register scratch1, 611 Register scratch2, 612 Label* miss_label) { 613 // a0 : value 614 Label exit; 615 616 // Stub never generated for non-global objects that require access 617 // checks. 618 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 619 620 int index = lookup->GetFieldIndex().field_index(); 621 622 // Adjust for the number of properties stored in the object. Even in the 623 // face of a transition we can use the old map here because the size of the 624 // object and the number of in-object properties is not going to change. 625 index -= object->map()->inobject_properties(); 626 627 Representation representation = lookup->representation(); 628 ASSERT(!representation.IsNone()); 629 if (FLAG_track_fields && representation.IsSmi()) { 630 __ JumpIfNotSmi(value_reg, miss_label); 631 } else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) { 632 __ JumpIfSmi(value_reg, miss_label); 633 } else if (FLAG_track_double_fields && representation.IsDouble()) { 634 // Load the double storage. 635 if (index < 0) { 636 int offset = object->map()->instance_size() + (index * kPointerSize); 637 __ lw(scratch1, FieldMemOperand(receiver_reg, offset)); 638 } else { 639 __ lw(scratch1, 640 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); 641 int offset = index * kPointerSize + FixedArray::kHeaderSize; 642 __ lw(scratch1, FieldMemOperand(scratch1, offset)); 643 } 644 645 // Store the value into the storage. 646 Label do_store, heap_number; 647 __ JumpIfNotSmi(value_reg, &heap_number); 648 __ SmiUntag(scratch2, value_reg); 649 __ mtc1(scratch2, f6); 650 __ cvt_d_w(f4, f6); 651 __ jmp(&do_store); 652 653 __ bind(&heap_number); 654 __ CheckMap(value_reg, scratch2, Heap::kHeapNumberMapRootIndex, 655 miss_label, DONT_DO_SMI_CHECK); 656 __ ldc1(f4, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); 657 658 __ bind(&do_store); 659 __ sdc1(f4, FieldMemOperand(scratch1, HeapNumber::kValueOffset)); 660 // Return the value (register v0). 661 ASSERT(value_reg.is(a0)); 662 __ Ret(USE_DELAY_SLOT); 663 __ mov(v0, a0); 664 return; 665 } 666 667 // TODO(verwaest): Share this code as a code stub. 668 SmiCheck smi_check = representation.IsTagged() 669 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 670 if (index < 0) { 671 // Set the property straight into the object. 672 int offset = object->map()->instance_size() + (index * kPointerSize); 673 __ sw(value_reg, FieldMemOperand(receiver_reg, offset)); 674 675 if (!FLAG_track_fields || !representation.IsSmi()) { 676 // Skip updating write barrier if storing a smi. 677 __ JumpIfSmi(value_reg, &exit); 678 679 // Update the write barrier for the array address. 680 // Pass the now unused name_reg as a scratch register. 681 __ mov(name_reg, value_reg); 682 __ RecordWriteField(receiver_reg, 683 offset, 684 name_reg, 685 scratch1, 686 kRAHasNotBeenSaved, 687 kDontSaveFPRegs, 688 EMIT_REMEMBERED_SET, 689 smi_check); 690 } 691 } else { 692 // Write to the properties array. 693 int offset = index * kPointerSize + FixedArray::kHeaderSize; 694 // Get the properties array. 695 __ lw(scratch1, 696 FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); 697 __ sw(value_reg, FieldMemOperand(scratch1, offset)); 698 699 if (!FLAG_track_fields || !representation.IsSmi()) { 700 // Skip updating write barrier if storing a smi. 701 __ JumpIfSmi(value_reg, &exit); 702 703 // Update the write barrier for the array address. 704 // Ok to clobber receiver_reg and name_reg, since we return. 705 __ mov(name_reg, value_reg); 706 __ RecordWriteField(scratch1, 707 offset, 708 name_reg, 709 receiver_reg, 710 kRAHasNotBeenSaved, 711 kDontSaveFPRegs, 712 EMIT_REMEMBERED_SET, 713 smi_check); 714 } 715 } 716 717 // Return the value (register v0). 718 ASSERT(value_reg.is(a0)); 719 __ bind(&exit); 720 __ Ret(USE_DELAY_SLOT); 721 __ mov(v0, a0); 722 } 723 724 725 void StoreStubCompiler::GenerateRestoreName(MacroAssembler* masm, 726 Label* label, 727 Handle<Name> name) { 728 if (!label->is_unused()) { 729 __ bind(label); 730 __ li(this->name(), Operand(name)); 731 } 732 } 733 734 735 static void PushInterceptorArguments(MacroAssembler* masm, 736 Register receiver, 737 Register holder, 738 Register name, 739 Handle<JSObject> holder_obj) { 740 STATIC_ASSERT(StubCache::kInterceptorArgsNameIndex == 0); 741 STATIC_ASSERT(StubCache::kInterceptorArgsInfoIndex == 1); 742 STATIC_ASSERT(StubCache::kInterceptorArgsThisIndex == 2); 743 STATIC_ASSERT(StubCache::kInterceptorArgsHolderIndex == 3); 744 STATIC_ASSERT(StubCache::kInterceptorArgsLength == 4); 745 __ push(name); 746 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); 747 ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); 748 Register scratch = name; 749 __ li(scratch, Operand(interceptor)); 750 __ Push(scratch, receiver, holder); 751 } 752 753 754 static void CompileCallLoadPropertyWithInterceptor( 755 MacroAssembler* masm, 756 Register receiver, 757 Register holder, 758 Register name, 759 Handle<JSObject> holder_obj, 760 IC::UtilityId id) { 761 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 762 __ CallExternalReference( 763 ExternalReference(IC_Utility(id), masm->isolate()), 764 StubCache::kInterceptorArgsLength); 765 } 766 767 768 static const int kFastApiCallArguments = FunctionCallbackArguments::kArgsLength; 769 770 // Reserves space for the extra arguments to API function in the 771 // caller's frame. 772 // 773 // These arguments are set by CheckPrototypes and GenerateFastApiDirectCall. 774 static void ReserveSpaceForFastApiCall(MacroAssembler* masm, 775 Register scratch) { 776 ASSERT(Smi::FromInt(0) == 0); 777 for (int i = 0; i < kFastApiCallArguments; i++) { 778 __ push(zero_reg); 779 } 780 } 781 782 783 // Undoes the effects of ReserveSpaceForFastApiCall. 784 static void FreeSpaceForFastApiCall(MacroAssembler* masm) { 785 __ Drop(kFastApiCallArguments); 786 } 787 788 789 static void GenerateFastApiDirectCall(MacroAssembler* masm, 790 const CallOptimization& optimization, 791 int argc, 792 bool restore_context) { 793 // ----------- S t a t e ------------- 794 // -- sp[0] - sp[24] : FunctionCallbackInfo, incl. 795 // : holder (set by CheckPrototypes) 796 // -- sp[28] : last JS argument 797 // -- ... 798 // -- sp[(argc + 6) * 4] : first JS argument 799 // -- sp[(argc + 7) * 4] : receiver 800 // ----------------------------------- 801 typedef FunctionCallbackArguments FCA; 802 // Save calling context. 803 __ sw(cp, MemOperand(sp, FCA::kContextSaveIndex * kPointerSize)); 804 // Get the function and setup the context. 805 Handle<JSFunction> function = optimization.constant_function(); 806 __ li(t1, function); 807 __ lw(cp, FieldMemOperand(t1, JSFunction::kContextOffset)); 808 __ sw(t1, MemOperand(sp, FCA::kCalleeIndex * kPointerSize)); 809 810 // Construct the FunctionCallbackInfo. 811 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 812 Handle<Object> call_data(api_call_info->data(), masm->isolate()); 813 if (masm->isolate()->heap()->InNewSpace(*call_data)) { 814 __ li(a0, api_call_info); 815 __ lw(t2, FieldMemOperand(a0, CallHandlerInfo::kDataOffset)); 816 } else { 817 __ li(t2, call_data); 818 } 819 // Store call data. 820 __ sw(t2, MemOperand(sp, FCA::kDataIndex * kPointerSize)); 821 // Store isolate. 822 __ li(t3, Operand(ExternalReference::isolate_address(masm->isolate()))); 823 __ sw(t3, MemOperand(sp, FCA::kIsolateIndex * kPointerSize)); 824 // Store ReturnValue default and ReturnValue. 825 __ LoadRoot(t1, Heap::kUndefinedValueRootIndex); 826 __ sw(t1, MemOperand(sp, FCA::kReturnValueOffset * kPointerSize)); 827 __ sw(t1, MemOperand(sp, FCA::kReturnValueDefaultValueIndex * kPointerSize)); 828 829 // Prepare arguments. 830 __ Move(a2, sp); 831 832 // Allocate the v8::Arguments structure in the arguments' space since 833 // it's not controlled by GC. 834 const int kApiStackSpace = 4; 835 836 FrameScope frame_scope(masm, StackFrame::MANUAL); 837 __ EnterExitFrame(false, kApiStackSpace); 838 839 // a0 = FunctionCallbackInfo& 840 // Arguments is built at sp + 1 (sp is a reserved spot for ra). 841 __ Addu(a0, sp, kPointerSize); 842 // FunctionCallbackInfo::implicit_args_ 843 __ sw(a2, MemOperand(a0, 0 * kPointerSize)); 844 // FunctionCallbackInfo::values_ 845 __ Addu(t0, a2, Operand((kFastApiCallArguments - 1 + argc) * kPointerSize)); 846 __ sw(t0, MemOperand(a0, 1 * kPointerSize)); 847 // FunctionCallbackInfo::length_ = argc 848 __ li(t0, Operand(argc)); 849 __ sw(t0, MemOperand(a0, 2 * kPointerSize)); 850 // FunctionCallbackInfo::is_construct_call = 0 851 __ sw(zero_reg, MemOperand(a0, 3 * kPointerSize)); 852 853 const int kStackUnwindSpace = argc + kFastApiCallArguments + 1; 854 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 855 ApiFunction fun(function_address); 856 ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; 857 ExternalReference ref = 858 ExternalReference(&fun, 859 type, 860 masm->isolate()); 861 Address thunk_address = FUNCTION_ADDR(&InvokeFunctionCallback); 862 ExternalReference::Type thunk_type = ExternalReference::PROFILING_API_CALL; 863 ApiFunction thunk_fun(thunk_address); 864 ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type, 865 masm->isolate()); 866 867 AllowExternalCallThatCantCauseGC scope(masm); 868 MemOperand context_restore_operand( 869 fp, (2 + FCA::kContextSaveIndex) * kPointerSize); 870 MemOperand return_value_operand( 871 fp, (2 + FCA::kReturnValueOffset) * kPointerSize); 872 873 __ CallApiFunctionAndReturn(ref, 874 function_address, 875 thunk_ref, 876 a1, 877 kStackUnwindSpace, 878 return_value_operand, 879 restore_context ? 880 &context_restore_operand : NULL); 881 } 882 883 884 // Generate call to api function. 885 static void GenerateFastApiCall(MacroAssembler* masm, 886 const CallOptimization& optimization, 887 Register receiver, 888 Register scratch, 889 int argc, 890 Register* values) { 891 ASSERT(optimization.is_simple_api_call()); 892 ASSERT(!receiver.is(scratch)); 893 894 typedef FunctionCallbackArguments FCA; 895 const int stack_space = kFastApiCallArguments + argc + 1; 896 // Assign stack space for the call arguments. 897 __ Subu(sp, sp, Operand(stack_space * kPointerSize)); 898 // Write holder to stack frame. 899 __ sw(receiver, MemOperand(sp, FCA::kHolderIndex * kPointerSize)); 900 // Write receiver to stack frame. 901 int index = stack_space - 1; 902 __ sw(receiver, MemOperand(sp, index * kPointerSize)); 903 // Write the arguments to stack frame. 904 for (int i = 0; i < argc; i++) { 905 ASSERT(!receiver.is(values[i])); 906 ASSERT(!scratch.is(values[i])); 907 __ sw(receiver, MemOperand(sp, index-- * kPointerSize)); 908 } 909 910 GenerateFastApiDirectCall(masm, optimization, argc, true); 911 } 912 913 914 class CallInterceptorCompiler BASE_EMBEDDED { 915 public: 916 CallInterceptorCompiler(CallStubCompiler* stub_compiler, 917 const ParameterCount& arguments, 918 Register name, 919 ExtraICState extra_ic_state) 920 : stub_compiler_(stub_compiler), 921 arguments_(arguments), 922 name_(name), 923 extra_ic_state_(extra_ic_state) {} 924 925 void Compile(MacroAssembler* masm, 926 Handle<JSObject> object, 927 Handle<JSObject> holder, 928 Handle<Name> name, 929 LookupResult* lookup, 930 Register receiver, 931 Register scratch1, 932 Register scratch2, 933 Register scratch3, 934 Label* miss) { 935 ASSERT(holder->HasNamedInterceptor()); 936 ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); 937 938 // Check that the receiver isn't a smi. 939 __ JumpIfSmi(receiver, miss); 940 CallOptimization optimization(lookup); 941 if (optimization.is_constant_call()) { 942 CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3, 943 holder, lookup, name, optimization, miss); 944 } else { 945 CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3, 946 name, holder, miss); 947 } 948 } 949 950 private: 951 void CompileCacheable(MacroAssembler* masm, 952 Handle<JSObject> object, 953 Register receiver, 954 Register scratch1, 955 Register scratch2, 956 Register scratch3, 957 Handle<JSObject> interceptor_holder, 958 LookupResult* lookup, 959 Handle<Name> name, 960 const CallOptimization& optimization, 961 Label* miss_label) { 962 ASSERT(optimization.is_constant_call()); 963 ASSERT(!lookup->holder()->IsGlobalObject()); 964 Counters* counters = masm->isolate()->counters(); 965 int depth1 = kInvalidProtoDepth; 966 int depth2 = kInvalidProtoDepth; 967 bool can_do_fast_api_call = false; 968 if (optimization.is_simple_api_call() && 969 !lookup->holder()->IsGlobalObject()) { 970 depth1 = optimization.GetPrototypeDepthOfExpectedType( 971 object, interceptor_holder); 972 if (depth1 == kInvalidProtoDepth) { 973 depth2 = optimization.GetPrototypeDepthOfExpectedType( 974 interceptor_holder, Handle<JSObject>(lookup->holder())); 975 } 976 can_do_fast_api_call = 977 depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth; 978 } 979 980 __ IncrementCounter(counters->call_const_interceptor(), 1, 981 scratch1, scratch2); 982 983 if (can_do_fast_api_call) { 984 __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1, 985 scratch1, scratch2); 986 ReserveSpaceForFastApiCall(masm, scratch1); 987 } 988 989 // Check that the maps from receiver to interceptor's holder 990 // haven't changed and thus we can invoke interceptor. 991 Label miss_cleanup; 992 Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; 993 Register holder = 994 stub_compiler_->CheckPrototypes( 995 IC::CurrentTypeOf(object, masm->isolate()), receiver, 996 interceptor_holder, scratch1, scratch2, scratch3, 997 name, depth1, miss); 998 999 // Invoke an interceptor and if it provides a value, 1000 // branch to |regular_invoke|. 1001 Label regular_invoke; 1002 LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2, 1003 ®ular_invoke); 1004 1005 // Interceptor returned nothing for this property. Try to use cached 1006 // constant function. 1007 1008 // Check that the maps from interceptor's holder to constant function's 1009 // holder haven't changed and thus we can use cached constant function. 1010 if (*interceptor_holder != lookup->holder()) { 1011 stub_compiler_->CheckPrototypes( 1012 IC::CurrentTypeOf(interceptor_holder, masm->isolate()), holder, 1013 handle(lookup->holder()), scratch1, scratch2, scratch3, 1014 name, depth2, miss); 1015 } else { 1016 // CheckPrototypes has a side effect of fetching a 'holder' 1017 // for API (object which is instanceof for the signature). It's 1018 // safe to omit it here, as if present, it should be fetched 1019 // by the previous CheckPrototypes. 1020 ASSERT(depth2 == kInvalidProtoDepth); 1021 } 1022 1023 // Invoke function. 1024 if (can_do_fast_api_call) { 1025 GenerateFastApiDirectCall( 1026 masm, optimization, arguments_.immediate(), false); 1027 } else { 1028 Handle<JSFunction> function = optimization.constant_function(); 1029 __ Move(a0, receiver); 1030 stub_compiler_->GenerateJumpFunction(object, function); 1031 } 1032 1033 // Deferred code for fast API call case---clean preallocated space. 1034 if (can_do_fast_api_call) { 1035 __ bind(&miss_cleanup); 1036 FreeSpaceForFastApiCall(masm); 1037 __ Branch(miss_label); 1038 } 1039 1040 // Invoke a regular function. 1041 __ bind(®ular_invoke); 1042 if (can_do_fast_api_call) { 1043 FreeSpaceForFastApiCall(masm); 1044 } 1045 } 1046 1047 void CompileRegular(MacroAssembler* masm, 1048 Handle<JSObject> object, 1049 Register receiver, 1050 Register scratch1, 1051 Register scratch2, 1052 Register scratch3, 1053 Handle<Name> name, 1054 Handle<JSObject> interceptor_holder, 1055 Label* miss_label) { 1056 Register holder = 1057 stub_compiler_->CheckPrototypes( 1058 IC::CurrentTypeOf(object, masm->isolate()), receiver, 1059 interceptor_holder, scratch1, scratch2, scratch3, name, miss_label); 1060 1061 // Call a runtime function to load the interceptor property. 1062 FrameScope scope(masm, StackFrame::INTERNAL); 1063 // Save the name_ register across the call. 1064 __ push(name_); 1065 1066 CompileCallLoadPropertyWithInterceptor( 1067 masm, receiver, holder, name_, interceptor_holder, 1068 IC::kLoadPropertyWithInterceptorForCall); 1069 1070 // Restore the name_ register. 1071 __ pop(name_); 1072 // Leave the internal frame. 1073 } 1074 1075 void LoadWithInterceptor(MacroAssembler* masm, 1076 Register receiver, 1077 Register holder, 1078 Handle<JSObject> holder_obj, 1079 Register scratch, 1080 Label* interceptor_succeeded) { 1081 { 1082 FrameScope scope(masm, StackFrame::INTERNAL); 1083 1084 __ Push(receiver, holder, name_); 1085 CompileCallLoadPropertyWithInterceptor( 1086 masm, receiver, holder, name_, holder_obj, 1087 IC::kLoadPropertyWithInterceptorOnly); 1088 __ pop(name_); 1089 __ pop(holder); 1090 __ pop(receiver); 1091 } 1092 // If interceptor returns no-result sentinel, call the constant function. 1093 __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); 1094 __ Branch(interceptor_succeeded, ne, v0, Operand(scratch)); 1095 } 1096 1097 CallStubCompiler* stub_compiler_; 1098 const ParameterCount& arguments_; 1099 Register name_; 1100 ExtraICState extra_ic_state_; 1101 }; 1102 1103 1104 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { 1105 __ Jump(code, RelocInfo::CODE_TARGET); 1106 } 1107 1108 1109 #undef __ 1110 #define __ ACCESS_MASM(masm()) 1111 1112 1113 Register StubCompiler::CheckPrototypes(Handle<Type> type, 1114 Register object_reg, 1115 Handle<JSObject> holder, 1116 Register holder_reg, 1117 Register scratch1, 1118 Register scratch2, 1119 Handle<Name> name, 1120 int save_at_depth, 1121 Label* miss, 1122 PrototypeCheckType check) { 1123 Handle<Map> receiver_map(IC::TypeToMap(*type, isolate())); 1124 // Make sure that the type feedback oracle harvests the receiver map. 1125 // TODO(svenpanne) Remove this hack when all ICs are reworked. 1126 __ li(scratch1, Operand(receiver_map)); 1127 1128 // Make sure there's no overlap between holder and object registers. 1129 ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 1130 ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) 1131 && !scratch2.is(scratch1)); 1132 1133 // Keep track of the current object in register reg. 1134 Register reg = object_reg; 1135 int depth = 0; 1136 1137 typedef FunctionCallbackArguments FCA; 1138 if (save_at_depth == depth) { 1139 __ sw(reg, MemOperand(sp, FCA::kHolderIndex * kPointerSize)); 1140 } 1141 1142 Handle<JSObject> current = Handle<JSObject>::null(); 1143 if (type->IsConstant()) current = Handle<JSObject>::cast(type->AsConstant()); 1144 Handle<JSObject> prototype = Handle<JSObject>::null(); 1145 Handle<Map> current_map = receiver_map; 1146 Handle<Map> holder_map(holder->map()); 1147 // Traverse the prototype chain and check the maps in the prototype chain for 1148 // fast and global objects or do negative lookup for normal objects. 1149 while (!current_map.is_identical_to(holder_map)) { 1150 ++depth; 1151 1152 // Only global objects and objects that do not require access 1153 // checks are allowed in stubs. 1154 ASSERT(current_map->IsJSGlobalProxyMap() || 1155 !current_map->is_access_check_needed()); 1156 1157 prototype = handle(JSObject::cast(current_map->prototype())); 1158 if (current_map->is_dictionary_map() && 1159 !current_map->IsJSGlobalObjectMap() && 1160 !current_map->IsJSGlobalProxyMap()) { 1161 if (!name->IsUniqueName()) { 1162 ASSERT(name->IsString()); 1163 name = factory()->InternalizeString(Handle<String>::cast(name)); 1164 } 1165 ASSERT(current.is_null() || 1166 current->property_dictionary()->FindEntry(*name) == 1167 NameDictionary::kNotFound); 1168 1169 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, 1170 scratch1, scratch2); 1171 1172 __ lw(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset)); 1173 reg = holder_reg; // From now on the object will be in holder_reg. 1174 __ lw(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset)); 1175 } else { 1176 Register map_reg = scratch1; 1177 if (depth != 1 || check == CHECK_ALL_MAPS) { 1178 // CheckMap implicitly loads the map of |reg| into |map_reg|. 1179 __ CheckMap(reg, map_reg, current_map, miss, DONT_DO_SMI_CHECK); 1180 } else { 1181 __ lw(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset)); 1182 } 1183 1184 // Check access rights to the global object. This has to happen after 1185 // the map check so that we know that the object is actually a global 1186 // object. 1187 if (current_map->IsJSGlobalProxyMap()) { 1188 __ CheckAccessGlobalProxy(reg, scratch2, miss); 1189 } else if (current_map->IsJSGlobalObjectMap()) { 1190 GenerateCheckPropertyCell( 1191 masm(), Handle<JSGlobalObject>::cast(current), name, 1192 scratch2, miss); 1193 } 1194 1195 reg = holder_reg; // From now on the object will be in holder_reg. 1196 1197 if (heap()->InNewSpace(*prototype)) { 1198 // The prototype is in new space; we cannot store a reference to it 1199 // in the code. Load it from the map. 1200 __ lw(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset)); 1201 } else { 1202 // The prototype is in old space; load it directly. 1203 __ li(reg, Operand(prototype)); 1204 } 1205 } 1206 1207 if (save_at_depth == depth) { 1208 __ sw(reg, MemOperand(sp, FCA::kHolderIndex * kPointerSize)); 1209 } 1210 1211 // Go to the next object in the prototype chain. 1212 current = prototype; 1213 current_map = handle(current->map()); 1214 } 1215 1216 // Log the check depth. 1217 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 1218 1219 if (depth != 0 || check == CHECK_ALL_MAPS) { 1220 // Check the holder map. 1221 __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK); 1222 } 1223 1224 // Perform security check for access to the global object. 1225 ASSERT(current_map->IsJSGlobalProxyMap() || 1226 !current_map->is_access_check_needed()); 1227 if (current_map->IsJSGlobalProxyMap()) { 1228 __ CheckAccessGlobalProxy(reg, scratch1, miss); 1229 } 1230 1231 // Return the register containing the holder. 1232 return reg; 1233 } 1234 1235 1236 void LoadStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 1237 if (!miss->is_unused()) { 1238 Label success; 1239 __ Branch(&success); 1240 __ bind(miss); 1241 TailCallBuiltin(masm(), MissBuiltin(kind())); 1242 __ bind(&success); 1243 } 1244 } 1245 1246 1247 void StoreStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 1248 if (!miss->is_unused()) { 1249 Label success; 1250 __ Branch(&success); 1251 GenerateRestoreName(masm(), miss, name); 1252 TailCallBuiltin(masm(), MissBuiltin(kind())); 1253 __ bind(&success); 1254 } 1255 } 1256 1257 1258 Register LoadStubCompiler::CallbackHandlerFrontend( 1259 Handle<Type> type, 1260 Register object_reg, 1261 Handle<JSObject> holder, 1262 Handle<Name> name, 1263 Handle<Object> callback) { 1264 Label miss; 1265 1266 Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); 1267 1268 if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { 1269 ASSERT(!reg.is(scratch2())); 1270 ASSERT(!reg.is(scratch3())); 1271 ASSERT(!reg.is(scratch4())); 1272 1273 // Load the properties dictionary. 1274 Register dictionary = scratch4(); 1275 __ lw(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset)); 1276 1277 // Probe the dictionary. 1278 Label probe_done; 1279 NameDictionaryLookupStub::GeneratePositiveLookup(masm(), 1280 &miss, 1281 &probe_done, 1282 dictionary, 1283 this->name(), 1284 scratch2(), 1285 scratch3()); 1286 __ bind(&probe_done); 1287 1288 // If probing finds an entry in the dictionary, scratch3 contains the 1289 // pointer into the dictionary. Check that the value is the callback. 1290 Register pointer = scratch3(); 1291 const int kElementsStartOffset = NameDictionary::kHeaderSize + 1292 NameDictionary::kElementsStartIndex * kPointerSize; 1293 const int kValueOffset = kElementsStartOffset + kPointerSize; 1294 __ lw(scratch2(), FieldMemOperand(pointer, kValueOffset)); 1295 __ Branch(&miss, ne, scratch2(), Operand(callback)); 1296 } 1297 1298 HandlerFrontendFooter(name, &miss); 1299 return reg; 1300 } 1301 1302 1303 void LoadStubCompiler::GenerateLoadField(Register reg, 1304 Handle<JSObject> holder, 1305 PropertyIndex field, 1306 Representation representation) { 1307 if (!reg.is(receiver())) __ mov(receiver(), reg); 1308 if (kind() == Code::LOAD_IC) { 1309 LoadFieldStub stub(field.is_inobject(holder), 1310 field.translate(holder), 1311 representation); 1312 GenerateTailCall(masm(), stub.GetCode(isolate())); 1313 } else { 1314 KeyedLoadFieldStub stub(field.is_inobject(holder), 1315 field.translate(holder), 1316 representation); 1317 GenerateTailCall(masm(), stub.GetCode(isolate())); 1318 } 1319 } 1320 1321 1322 void LoadStubCompiler::GenerateLoadConstant(Handle<Object> value) { 1323 // Return the constant value. 1324 __ li(v0, value); 1325 __ Ret(); 1326 } 1327 1328 1329 void LoadStubCompiler::GenerateLoadCallback( 1330 const CallOptimization& call_optimization) { 1331 GenerateFastApiCall( 1332 masm(), call_optimization, receiver(), scratch3(), 0, NULL); 1333 } 1334 1335 1336 void LoadStubCompiler::GenerateLoadCallback( 1337 Register reg, 1338 Handle<ExecutableAccessorInfo> callback) { 1339 // Build AccessorInfo::args_ list on the stack and push property name below 1340 // the exit frame to make GC aware of them and store pointers to them. 1341 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); 1342 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); 1343 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); 1344 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); 1345 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); 1346 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); 1347 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6); 1348 ASSERT(!scratch2().is(reg)); 1349 ASSERT(!scratch3().is(reg)); 1350 ASSERT(!scratch4().is(reg)); 1351 __ push(receiver()); 1352 if (heap()->InNewSpace(callback->data())) { 1353 __ li(scratch3(), callback); 1354 __ lw(scratch3(), FieldMemOperand(scratch3(), 1355 ExecutableAccessorInfo::kDataOffset)); 1356 } else { 1357 __ li(scratch3(), Handle<Object>(callback->data(), isolate())); 1358 } 1359 __ Subu(sp, sp, 6 * kPointerSize); 1360 __ sw(scratch3(), MemOperand(sp, 5 * kPointerSize)); 1361 __ LoadRoot(scratch3(), Heap::kUndefinedValueRootIndex); 1362 __ sw(scratch3(), MemOperand(sp, 4 * kPointerSize)); 1363 __ sw(scratch3(), MemOperand(sp, 3 * kPointerSize)); 1364 __ li(scratch4(), 1365 Operand(ExternalReference::isolate_address(isolate()))); 1366 __ sw(scratch4(), MemOperand(sp, 2 * kPointerSize)); 1367 __ sw(reg, MemOperand(sp, 1 * kPointerSize)); 1368 __ sw(name(), MemOperand(sp, 0 * kPointerSize)); 1369 __ Addu(scratch2(), sp, 1 * kPointerSize); 1370 1371 __ mov(a2, scratch2()); // Saved in case scratch2 == a1. 1372 __ mov(a0, sp); // (first argument - a0) = Handle<Name> 1373 1374 const int kApiStackSpace = 1; 1375 FrameScope frame_scope(masm(), StackFrame::MANUAL); 1376 __ EnterExitFrame(false, kApiStackSpace); 1377 1378 // Create PropertyAccessorInfo instance on the stack above the exit frame with 1379 // scratch2 (internal::Object** args_) as the data. 1380 __ sw(a2, MemOperand(sp, kPointerSize)); 1381 // (second argument - a1) = AccessorInfo& 1382 __ Addu(a1, sp, kPointerSize); 1383 1384 const int kStackUnwindSpace = PropertyCallbackArguments::kArgsLength + 1; 1385 Address getter_address = v8::ToCData<Address>(callback->getter()); 1386 ApiFunction fun(getter_address); 1387 ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL; 1388 ExternalReference ref = ExternalReference(&fun, type, isolate()); 1389 1390 Address thunk_address = FUNCTION_ADDR(&InvokeAccessorGetterCallback); 1391 ExternalReference::Type thunk_type = 1392 ExternalReference::PROFILING_GETTER_CALL; 1393 ApiFunction thunk_fun(thunk_address); 1394 ExternalReference thunk_ref = ExternalReference(&thunk_fun, thunk_type, 1395 isolate()); 1396 __ CallApiFunctionAndReturn(ref, 1397 getter_address, 1398 thunk_ref, 1399 a2, 1400 kStackUnwindSpace, 1401 MemOperand(fp, 6 * kPointerSize), 1402 NULL); 1403 } 1404 1405 1406 void LoadStubCompiler::GenerateLoadInterceptor( 1407 Register holder_reg, 1408 Handle<Object> object, 1409 Handle<JSObject> interceptor_holder, 1410 LookupResult* lookup, 1411 Handle<Name> name) { 1412 ASSERT(interceptor_holder->HasNamedInterceptor()); 1413 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); 1414 1415 // So far the most popular follow ups for interceptor loads are FIELD 1416 // and CALLBACKS, so inline only them, other cases may be added 1417 // later. 1418 bool compile_followup_inline = false; 1419 if (lookup->IsFound() && lookup->IsCacheable()) { 1420 if (lookup->IsField()) { 1421 compile_followup_inline = true; 1422 } else if (lookup->type() == CALLBACKS && 1423 lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { 1424 ExecutableAccessorInfo* callback = 1425 ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); 1426 compile_followup_inline = callback->getter() != NULL && 1427 callback->IsCompatibleReceiver(*object); 1428 } 1429 } 1430 1431 if (compile_followup_inline) { 1432 // Compile the interceptor call, followed by inline code to load the 1433 // property from further up the prototype chain if the call fails. 1434 // Check that the maps haven't changed. 1435 ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 1436 1437 // Preserve the receiver register explicitly whenever it is different from 1438 // the holder and it is needed should the interceptor return without any 1439 // result. The CALLBACKS case needs the receiver to be passed into C++ code, 1440 // the FIELD case might cause a miss during the prototype check. 1441 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); 1442 bool must_preserve_receiver_reg = !receiver().is(holder_reg) && 1443 (lookup->type() == CALLBACKS || must_perfrom_prototype_check); 1444 1445 // Save necessary data before invoking an interceptor. 1446 // Requires a frame to make GC aware of pushed pointers. 1447 { 1448 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 1449 if (must_preserve_receiver_reg) { 1450 __ Push(receiver(), holder_reg, this->name()); 1451 } else { 1452 __ Push(holder_reg, this->name()); 1453 } 1454 // Invoke an interceptor. Note: map checks from receiver to 1455 // interceptor's holder has been compiled before (see a caller 1456 // of this method). 1457 CompileCallLoadPropertyWithInterceptor( 1458 masm(), receiver(), holder_reg, this->name(), interceptor_holder, 1459 IC::kLoadPropertyWithInterceptorOnly); 1460 1461 // Check if interceptor provided a value for property. If it's 1462 // the case, return immediately. 1463 Label interceptor_failed; 1464 __ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex); 1465 __ Branch(&interceptor_failed, eq, v0, Operand(scratch1())); 1466 frame_scope.GenerateLeaveFrame(); 1467 __ Ret(); 1468 1469 __ bind(&interceptor_failed); 1470 __ pop(this->name()); 1471 __ pop(holder_reg); 1472 if (must_preserve_receiver_reg) { 1473 __ pop(receiver()); 1474 } 1475 // Leave the internal frame. 1476 } 1477 GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); 1478 } else { // !compile_followup_inline 1479 // Call the runtime system to load the interceptor. 1480 // Check that the maps haven't changed. 1481 PushInterceptorArguments(masm(), receiver(), holder_reg, 1482 this->name(), interceptor_holder); 1483 1484 ExternalReference ref = ExternalReference( 1485 IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate()); 1486 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); 1487 } 1488 } 1489 1490 1491 void CallStubCompiler::GenerateNameCheck(Handle<Name> name, Label* miss) { 1492 if (kind_ == Code::KEYED_CALL_IC) { 1493 __ Branch(miss, ne, a2, Operand(name)); 1494 } 1495 } 1496 1497 1498 void CallStubCompiler::GenerateFunctionCheck(Register function, 1499 Register scratch, 1500 Label* miss) { 1501 __ JumpIfSmi(function, miss); 1502 __ GetObjectType(function, scratch, scratch); 1503 __ Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE)); 1504 } 1505 1506 1507 void CallStubCompiler::GenerateLoadFunctionFromCell( 1508 Handle<Cell> cell, 1509 Handle<JSFunction> function, 1510 Label* miss) { 1511 // Get the value from the cell. 1512 __ li(a3, Operand(cell)); 1513 __ lw(a1, FieldMemOperand(a3, Cell::kValueOffset)); 1514 1515 // Check that the cell contains the same function. 1516 if (heap()->InNewSpace(*function)) { 1517 // We can't embed a pointer to a function in new space so we have 1518 // to verify that the shared function info is unchanged. This has 1519 // the nice side effect that multiple closures based on the same 1520 // function can all use this call IC. Before we load through the 1521 // function, we have to verify that it still is a function. 1522 GenerateFunctionCheck(a1, a3, miss); 1523 1524 // Check the shared function info. Make sure it hasn't changed. 1525 __ li(a3, Handle<SharedFunctionInfo>(function->shared())); 1526 __ lw(t0, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); 1527 __ Branch(miss, ne, t0, Operand(a3)); 1528 } else { 1529 __ Branch(miss, ne, a1, Operand(function)); 1530 } 1531 } 1532 1533 1534 void CallStubCompiler::GenerateMissBranch() { 1535 Handle<Code> code = 1536 isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(), 1537 kind_, 1538 extra_state()); 1539 __ Jump(code, RelocInfo::CODE_TARGET); 1540 } 1541 1542 1543 Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object, 1544 Handle<JSObject> holder, 1545 PropertyIndex index, 1546 Handle<Name> name) { 1547 Label miss; 1548 1549 Register reg = HandlerFrontendHeader( 1550 object, holder, name, RECEIVER_MAP_CHECK, &miss); 1551 GenerateFastPropertyLoad(masm(), a1, reg, index.is_inobject(holder), 1552 index.translate(holder), Representation::Tagged()); 1553 GenerateJumpFunction(object, a1, &miss); 1554 1555 HandlerFrontendFooter(&miss); 1556 1557 // Return the generated code. 1558 return GetCode(Code::FAST, name); 1559 } 1560 1561 1562 Handle<Code> CallStubCompiler::CompileArrayCodeCall( 1563 Handle<Object> object, 1564 Handle<JSObject> holder, 1565 Handle<Cell> cell, 1566 Handle<JSFunction> function, 1567 Handle<String> name, 1568 Code::StubType type) { 1569 Label miss; 1570 1571 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 1572 if (!cell.is_null()) { 1573 ASSERT(cell->value() == *function); 1574 GenerateLoadFunctionFromCell(cell, function, &miss); 1575 } 1576 1577 Handle<AllocationSite> site = isolate()->factory()->NewAllocationSite(); 1578 site->SetElementsKind(GetInitialFastElementsKind()); 1579 Handle<Cell> site_feedback_cell = isolate()->factory()->NewCell(site); 1580 const int argc = arguments().immediate(); 1581 __ li(a0, Operand(argc)); 1582 __ li(a2, Operand(site_feedback_cell)); 1583 __ li(a1, Operand(function)); 1584 1585 ArrayConstructorStub stub(isolate()); 1586 __ TailCallStub(&stub); 1587 1588 HandlerFrontendFooter(&miss); 1589 1590 // Return the generated code. 1591 return GetCode(type, name); 1592 } 1593 1594 1595 Handle<Code> CallStubCompiler::CompileArrayPushCall( 1596 Handle<Object> object, 1597 Handle<JSObject> holder, 1598 Handle<Cell> cell, 1599 Handle<JSFunction> function, 1600 Handle<String> name, 1601 Code::StubType type) { 1602 // If object is not an array or is observed or sealed, bail out to regular 1603 // call. 1604 if (!object->IsJSArray() || 1605 !cell.is_null() || 1606 Handle<JSArray>::cast(object)->map()->is_observed() || 1607 !Handle<JSArray>::cast(object)->map()->is_extensible()) { 1608 return Handle<Code>::null(); 1609 } 1610 1611 Label miss; 1612 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 1613 Register receiver = a0; 1614 Register scratch = a1; 1615 1616 const int argc = arguments().immediate(); 1617 1618 if (argc == 0) { 1619 // Nothing to do, just return the length. 1620 __ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1621 __ DropAndRet(argc + 1); 1622 } else { 1623 Label call_builtin; 1624 if (argc == 1) { // Otherwise fall through to call the builtin. 1625 Label attempt_to_grow_elements, with_write_barrier, check_double; 1626 1627 Register elements = t2; 1628 Register end_elements = t1; 1629 // Get the elements array of the object. 1630 __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); 1631 1632 // Check that the elements are in fast mode and writable. 1633 __ CheckMap(elements, 1634 scratch, 1635 Heap::kFixedArrayMapRootIndex, 1636 &check_double, 1637 DONT_DO_SMI_CHECK); 1638 1639 // Get the array's length into scratch and calculate new length. 1640 __ lw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1641 STATIC_ASSERT(kSmiTagSize == 1); 1642 STATIC_ASSERT(kSmiTag == 0); 1643 __ Addu(scratch, scratch, Operand(Smi::FromInt(argc))); 1644 1645 // Get the elements' length. 1646 __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1647 1648 // Check if we could survive without allocation. 1649 __ Branch(&attempt_to_grow_elements, gt, scratch, Operand(t0)); 1650 1651 // Check if value is a smi. 1652 __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize)); 1653 __ JumpIfNotSmi(t0, &with_write_barrier); 1654 1655 // Save new length. 1656 __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1657 1658 // Store the value. 1659 // We may need a register containing the address end_elements below, 1660 // so write back the value in end_elements. 1661 __ sll(end_elements, scratch, kPointerSizeLog2 - kSmiTagSize); 1662 __ Addu(end_elements, elements, end_elements); 1663 const int kEndElementsOffset = 1664 FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; 1665 __ Addu(end_elements, end_elements, kEndElementsOffset); 1666 __ sw(t0, MemOperand(end_elements)); 1667 1668 // Check for a smi. 1669 __ mov(v0, scratch); 1670 __ DropAndRet(argc + 1); 1671 1672 __ bind(&check_double); 1673 1674 // Check that the elements are in fast mode and writable. 1675 __ CheckMap(elements, 1676 scratch, 1677 Heap::kFixedDoubleArrayMapRootIndex, 1678 &call_builtin, 1679 DONT_DO_SMI_CHECK); 1680 1681 // Get the array's length into scratch and calculate new length. 1682 __ lw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1683 STATIC_ASSERT(kSmiTagSize == 1); 1684 STATIC_ASSERT(kSmiTag == 0); 1685 __ Addu(scratch, scratch, Operand(Smi::FromInt(argc))); 1686 1687 // Get the elements' length. 1688 __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1689 1690 // Check if we could survive without allocation. 1691 __ Branch(&call_builtin, gt, scratch, Operand(t0)); 1692 1693 __ lw(t0, MemOperand(sp, (argc - 1) * kPointerSize)); 1694 __ StoreNumberToDoubleElements( 1695 t0, scratch, elements, a3, t1, a2, 1696 &call_builtin, argc * kDoubleSize); 1697 1698 // Save new length. 1699 __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1700 1701 __ mov(v0, scratch); 1702 __ DropAndRet(argc + 1); 1703 1704 __ bind(&with_write_barrier); 1705 1706 __ lw(a3, FieldMemOperand(receiver, HeapObject::kMapOffset)); 1707 1708 if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) { 1709 Label fast_object, not_fast_object; 1710 __ CheckFastObjectElements(a3, t3, ¬_fast_object); 1711 __ jmp(&fast_object); 1712 // In case of fast smi-only, convert to fast object, otherwise bail out. 1713 __ bind(¬_fast_object); 1714 __ CheckFastSmiElements(a3, t3, &call_builtin); 1715 1716 __ lw(t3, FieldMemOperand(t0, HeapObject::kMapOffset)); 1717 __ LoadRoot(at, Heap::kHeapNumberMapRootIndex); 1718 __ Branch(&call_builtin, eq, t3, Operand(at)); 1719 // edx: receiver 1720 // a3: map 1721 Label try_holey_map; 1722 __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, 1723 FAST_ELEMENTS, 1724 a3, 1725 t3, 1726 &try_holey_map); 1727 __ mov(a2, receiver); 1728 ElementsTransitionGenerator:: 1729 GenerateMapChangeElementsTransition(masm(), 1730 DONT_TRACK_ALLOCATION_SITE, 1731 NULL); 1732 __ jmp(&fast_object); 1733 1734 __ bind(&try_holey_map); 1735 __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS, 1736 FAST_HOLEY_ELEMENTS, 1737 a3, 1738 t3, 1739 &call_builtin); 1740 __ mov(a2, receiver); 1741 ElementsTransitionGenerator:: 1742 GenerateMapChangeElementsTransition(masm(), 1743 DONT_TRACK_ALLOCATION_SITE, 1744 NULL); 1745 __ bind(&fast_object); 1746 } else { 1747 __ CheckFastObjectElements(a3, a3, &call_builtin); 1748 } 1749 1750 // Save new length. 1751 __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1752 1753 // Store the value. 1754 // We may need a register containing the address end_elements below, 1755 // so write back the value in end_elements. 1756 __ sll(end_elements, scratch, kPointerSizeLog2 - kSmiTagSize); 1757 __ Addu(end_elements, elements, end_elements); 1758 __ Addu(end_elements, end_elements, kEndElementsOffset); 1759 __ sw(t0, MemOperand(end_elements)); 1760 1761 __ RecordWrite(elements, 1762 end_elements, 1763 t0, 1764 kRAHasNotBeenSaved, 1765 kDontSaveFPRegs, 1766 EMIT_REMEMBERED_SET, 1767 OMIT_SMI_CHECK); 1768 __ mov(v0, scratch); 1769 __ DropAndRet(argc + 1); 1770 1771 __ bind(&attempt_to_grow_elements); 1772 // scratch: array's length + 1. 1773 // t0: elements' length. 1774 1775 if (!FLAG_inline_new) { 1776 __ Branch(&call_builtin); 1777 } 1778 1779 __ lw(a2, MemOperand(sp, (argc - 1) * kPointerSize)); 1780 // Growing elements that are SMI-only requires special handling in case 1781 // the new element is non-Smi. For now, delegate to the builtin. 1782 Label no_fast_elements_check; 1783 __ JumpIfSmi(a2, &no_fast_elements_check); 1784 __ lw(t3, FieldMemOperand(receiver, HeapObject::kMapOffset)); 1785 __ CheckFastObjectElements(t3, t3, &call_builtin); 1786 __ bind(&no_fast_elements_check); 1787 1788 ExternalReference new_space_allocation_top = 1789 ExternalReference::new_space_allocation_top_address(isolate()); 1790 ExternalReference new_space_allocation_limit = 1791 ExternalReference::new_space_allocation_limit_address(isolate()); 1792 1793 const int kAllocationDelta = 4; 1794 // Load top and check if it is the end of elements. 1795 __ sll(end_elements, scratch, kPointerSizeLog2 - kSmiTagSize); 1796 __ Addu(end_elements, elements, end_elements); 1797 __ Addu(end_elements, end_elements, Operand(kEndElementsOffset)); 1798 __ li(t3, Operand(new_space_allocation_top)); 1799 __ lw(a3, MemOperand(t3)); 1800 __ Branch(&call_builtin, ne, end_elements, Operand(a3)); 1801 1802 __ li(t5, Operand(new_space_allocation_limit)); 1803 __ lw(t5, MemOperand(t5)); 1804 __ Addu(a3, a3, Operand(kAllocationDelta * kPointerSize)); 1805 __ Branch(&call_builtin, hi, a3, Operand(t5)); 1806 1807 // We fit and could grow elements. 1808 // Update new_space_allocation_top. 1809 __ sw(a3, MemOperand(t3)); 1810 // Push the argument. 1811 __ sw(a2, MemOperand(end_elements)); 1812 // Fill the rest with holes. 1813 __ LoadRoot(a3, Heap::kTheHoleValueRootIndex); 1814 for (int i = 1; i < kAllocationDelta; i++) { 1815 __ sw(a3, MemOperand(end_elements, i * kPointerSize)); 1816 } 1817 1818 // Update elements' and array's sizes. 1819 __ sw(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1820 __ Addu(t0, t0, Operand(Smi::FromInt(kAllocationDelta))); 1821 __ sw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1822 1823 // Elements are in new space, so write barrier is not required. 1824 __ mov(v0, scratch); 1825 __ DropAndRet(argc + 1); 1826 } 1827 __ bind(&call_builtin); 1828 __ TailCallExternalReference( 1829 ExternalReference(Builtins::c_ArrayPush, isolate()), argc + 1, 1); 1830 } 1831 1832 HandlerFrontendFooter(&miss); 1833 1834 // Return the generated code. 1835 return GetCode(type, name); 1836 } 1837 1838 1839 Handle<Code> CallStubCompiler::CompileArrayPopCall( 1840 Handle<Object> object, 1841 Handle<JSObject> holder, 1842 Handle<Cell> cell, 1843 Handle<JSFunction> function, 1844 Handle<String> name, 1845 Code::StubType type) { 1846 // If object is not an array or is observed or sealed, bail out to regular 1847 // call. 1848 if (!object->IsJSArray() || 1849 !cell.is_null() || 1850 Handle<JSArray>::cast(object)->map()->is_observed() || 1851 !Handle<JSArray>::cast(object)->map()->is_extensible()) { 1852 return Handle<Code>::null(); 1853 } 1854 1855 Label miss, return_undefined, call_builtin; 1856 Register receiver = a0; 1857 Register scratch = a1; 1858 Register elements = a3; 1859 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 1860 1861 // Get the elements array of the object. 1862 __ lw(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); 1863 1864 // Check that the elements are in fast mode and writable. 1865 __ CheckMap(elements, 1866 scratch, 1867 Heap::kFixedArrayMapRootIndex, 1868 &call_builtin, 1869 DONT_DO_SMI_CHECK); 1870 1871 // Get the array's length into t0 and calculate new length. 1872 __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1873 __ Subu(t0, t0, Operand(Smi::FromInt(1))); 1874 __ Branch(&return_undefined, lt, t0, Operand(zero_reg)); 1875 1876 // Get the last element. 1877 __ LoadRoot(t2, Heap::kTheHoleValueRootIndex); 1878 STATIC_ASSERT(kSmiTagSize == 1); 1879 STATIC_ASSERT(kSmiTag == 0); 1880 // We can't address the last element in one operation. Compute the more 1881 // expensive shift first, and use an offset later on. 1882 __ sll(t1, t0, kPointerSizeLog2 - kSmiTagSize); 1883 __ Addu(elements, elements, t1); 1884 __ lw(scratch, FieldMemOperand(elements, FixedArray::kHeaderSize)); 1885 __ Branch(&call_builtin, eq, scratch, Operand(t2)); 1886 1887 // Set the array's length. 1888 __ sw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1889 1890 // Fill with the hole. 1891 __ sw(t2, FieldMemOperand(elements, FixedArray::kHeaderSize)); 1892 const int argc = arguments().immediate(); 1893 __ mov(v0, scratch); 1894 __ DropAndRet(argc + 1); 1895 1896 __ bind(&return_undefined); 1897 __ LoadRoot(v0, Heap::kUndefinedValueRootIndex); 1898 __ DropAndRet(argc + 1); 1899 1900 __ bind(&call_builtin); 1901 __ TailCallExternalReference( 1902 ExternalReference(Builtins::c_ArrayPop, isolate()), argc + 1, 1); 1903 1904 HandlerFrontendFooter(&miss); 1905 1906 // Return the generated code. 1907 return GetCode(type, name); 1908 } 1909 1910 1911 Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall( 1912 Handle<Object> object, 1913 Handle<JSObject> holder, 1914 Handle<Cell> cell, 1915 Handle<JSFunction> function, 1916 Handle<String> name, 1917 Code::StubType type) { 1918 // If object is not a string, bail out to regular call. 1919 if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); 1920 1921 Label miss; 1922 Label name_miss; 1923 Label index_out_of_range; 1924 1925 Label* index_out_of_range_label = &index_out_of_range; 1926 1927 if (kind_ == Code::CALL_IC && 1928 (CallICBase::StringStubState::decode(extra_state()) == 1929 DEFAULT_STRING_STUB)) { 1930 index_out_of_range_label = &miss; 1931 } 1932 1933 HandlerFrontendHeader(object, holder, name, STRING_CHECK, &name_miss); 1934 1935 Register receiver = a0; 1936 Register index = t1; 1937 Register result = a1; 1938 const int argc = arguments().immediate(); 1939 __ lw(receiver, MemOperand(sp, argc * kPointerSize)); 1940 if (argc > 0) { 1941 __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize)); 1942 } else { 1943 __ LoadRoot(index, Heap::kUndefinedValueRootIndex); 1944 } 1945 1946 StringCharCodeAtGenerator generator(receiver, 1947 index, 1948 result, 1949 &miss, // When not a string. 1950 &miss, // When not a number. 1951 index_out_of_range_label, 1952 STRING_INDEX_IS_NUMBER); 1953 generator.GenerateFast(masm()); 1954 __ mov(v0, result); 1955 __ DropAndRet(argc + 1); 1956 1957 StubRuntimeCallHelper call_helper; 1958 generator.GenerateSlow(masm(), call_helper); 1959 1960 if (index_out_of_range.is_linked()) { 1961 __ bind(&index_out_of_range); 1962 __ LoadRoot(v0, Heap::kNanValueRootIndex); 1963 __ DropAndRet(argc + 1); 1964 } 1965 1966 __ bind(&miss); 1967 // Restore function name in a2. 1968 __ li(a2, name); 1969 HandlerFrontendFooter(&name_miss); 1970 1971 // Return the generated code. 1972 return GetCode(type, name); 1973 } 1974 1975 1976 Handle<Code> CallStubCompiler::CompileStringCharAtCall( 1977 Handle<Object> object, 1978 Handle<JSObject> holder, 1979 Handle<Cell> cell, 1980 Handle<JSFunction> function, 1981 Handle<String> name, 1982 Code::StubType type) { 1983 // If object is not a string, bail out to regular call. 1984 if (!object->IsString() || !cell.is_null()) return Handle<Code>::null(); 1985 1986 const int argc = arguments().immediate(); 1987 Label miss; 1988 Label name_miss; 1989 Label index_out_of_range; 1990 Label* index_out_of_range_label = &index_out_of_range; 1991 if (kind_ == Code::CALL_IC && 1992 (CallICBase::StringStubState::decode(extra_state()) == 1993 DEFAULT_STRING_STUB)) { 1994 index_out_of_range_label = &miss; 1995 } 1996 1997 HandlerFrontendHeader(object, holder, name, STRING_CHECK, &name_miss); 1998 1999 Register receiver = a0; 2000 Register index = t1; 2001 Register scratch = a3; 2002 Register result = a1; 2003 if (argc > 0) { 2004 __ lw(index, MemOperand(sp, (argc - 1) * kPointerSize)); 2005 } else { 2006 __ LoadRoot(index, Heap::kUndefinedValueRootIndex); 2007 } 2008 2009 StringCharAtGenerator generator(receiver, 2010 index, 2011 scratch, 2012 result, 2013 &miss, // When not a string. 2014 &miss, // When not a number. 2015 index_out_of_range_label, 2016 STRING_INDEX_IS_NUMBER); 2017 generator.GenerateFast(masm()); 2018 __ mov(v0, result); 2019 __ DropAndRet(argc + 1); 2020 2021 StubRuntimeCallHelper call_helper; 2022 generator.GenerateSlow(masm(), call_helper); 2023 2024 if (index_out_of_range.is_linked()) { 2025 __ bind(&index_out_of_range); 2026 __ LoadRoot(v0, Heap::kempty_stringRootIndex); 2027 __ DropAndRet(argc + 1); 2028 } 2029 2030 __ bind(&miss); 2031 // Restore function name in a2. 2032 __ li(a2, name); 2033 HandlerFrontendFooter(&name_miss); 2034 2035 // Return the generated code. 2036 return GetCode(type, name); 2037 } 2038 2039 2040 Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall( 2041 Handle<Object> object, 2042 Handle<JSObject> holder, 2043 Handle<Cell> cell, 2044 Handle<JSFunction> function, 2045 Handle<String> name, 2046 Code::StubType type) { 2047 const int argc = arguments().immediate(); 2048 2049 // If the object is not a JSObject or we got an unexpected number of 2050 // arguments, bail out to the regular call. 2051 if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); 2052 2053 Label miss; 2054 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 2055 if (!cell.is_null()) { 2056 ASSERT(cell->value() == *function); 2057 GenerateLoadFunctionFromCell(cell, function, &miss); 2058 } 2059 2060 // Load the char code argument. 2061 Register code = a1; 2062 __ lw(code, MemOperand(sp, 0 * kPointerSize)); 2063 2064 // Check the code is a smi. 2065 Label slow; 2066 STATIC_ASSERT(kSmiTag == 0); 2067 __ JumpIfNotSmi(code, &slow); 2068 2069 // Convert the smi code to uint16. 2070 __ And(code, code, Operand(Smi::FromInt(0xffff))); 2071 2072 StringCharFromCodeGenerator generator(code, v0); 2073 generator.GenerateFast(masm()); 2074 __ DropAndRet(argc + 1); 2075 2076 StubRuntimeCallHelper call_helper; 2077 generator.GenerateSlow(masm(), call_helper); 2078 2079 __ bind(&slow); 2080 // We do not have to patch the receiver because the function makes no use of 2081 // it. 2082 GenerateJumpFunctionIgnoreReceiver(function); 2083 2084 HandlerFrontendFooter(&miss); 2085 2086 // Return the generated code. 2087 return GetCode(type, name); 2088 } 2089 2090 2091 Handle<Code> CallStubCompiler::CompileMathFloorCall( 2092 Handle<Object> object, 2093 Handle<JSObject> holder, 2094 Handle<Cell> cell, 2095 Handle<JSFunction> function, 2096 Handle<String> name, 2097 Code::StubType type) { 2098 const int argc = arguments().immediate(); 2099 // If the object is not a JSObject or we got an unexpected number of 2100 // arguments, bail out to the regular call. 2101 if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); 2102 2103 Label miss, slow; 2104 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 2105 if (!cell.is_null()) { 2106 ASSERT(cell->value() == *function); 2107 GenerateLoadFunctionFromCell(cell, function, &miss); 2108 } 2109 2110 // Load the (only) argument into v0. 2111 __ lw(v0, MemOperand(sp, 0 * kPointerSize)); 2112 2113 // If the argument is a smi, just return. 2114 STATIC_ASSERT(kSmiTag == 0); 2115 __ SmiTst(v0, t0); 2116 __ DropAndRet(argc + 1, eq, t0, Operand(zero_reg)); 2117 2118 __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK); 2119 2120 Label wont_fit_smi, no_fpu_error, restore_fcsr_and_return; 2121 2122 // If fpu is enabled, we use the floor instruction. 2123 2124 // Load the HeapNumber value. 2125 __ ldc1(f0, FieldMemOperand(v0, HeapNumber::kValueOffset)); 2126 2127 // Backup FCSR. 2128 __ cfc1(a3, FCSR); 2129 // Clearing FCSR clears the exception mask with no side-effects. 2130 __ ctc1(zero_reg, FCSR); 2131 // Convert the argument to an integer. 2132 __ floor_w_d(f0, f0); 2133 2134 // Start checking for special cases. 2135 // Get the argument exponent and clear the sign bit. 2136 __ lw(t1, FieldMemOperand(v0, HeapNumber::kValueOffset + kPointerSize)); 2137 __ And(t2, t1, Operand(~HeapNumber::kSignMask)); 2138 __ srl(t2, t2, HeapNumber::kMantissaBitsInTopWord); 2139 2140 // Retrieve FCSR and check for fpu errors. 2141 __ cfc1(t5, FCSR); 2142 __ And(t5, t5, Operand(kFCSRExceptionFlagMask)); 2143 __ Branch(&no_fpu_error, eq, t5, Operand(zero_reg)); 2144 2145 // Check for NaN, Infinity, and -Infinity. 2146 // They are invariant through a Math.Floor call, so just 2147 // return the original argument. 2148 __ Subu(t3, t2, Operand(HeapNumber::kExponentMask 2149 >> HeapNumber::kMantissaBitsInTopWord)); 2150 __ Branch(&restore_fcsr_and_return, eq, t3, Operand(zero_reg)); 2151 // We had an overflow or underflow in the conversion. Check if we 2152 // have a big exponent. 2153 // If greater or equal, the argument is already round and in v0. 2154 __ Branch(&restore_fcsr_and_return, ge, t3, 2155 Operand(HeapNumber::kMantissaBits)); 2156 __ Branch(&wont_fit_smi); 2157 2158 __ bind(&no_fpu_error); 2159 // Move the result back to v0. 2160 __ mfc1(v0, f0); 2161 // Check if the result fits into a smi. 2162 __ Addu(a1, v0, Operand(0x40000000)); 2163 __ Branch(&wont_fit_smi, lt, a1, Operand(zero_reg)); 2164 // Tag the result. 2165 STATIC_ASSERT(kSmiTag == 0); 2166 __ sll(v0, v0, kSmiTagSize); 2167 2168 // Check for -0. 2169 __ Branch(&restore_fcsr_and_return, ne, v0, Operand(zero_reg)); 2170 // t1 already holds the HeapNumber exponent. 2171 __ And(t0, t1, Operand(HeapNumber::kSignMask)); 2172 // If our HeapNumber is negative it was -0, so load its address and return. 2173 // Else v0 is loaded with 0, so we can also just return. 2174 __ Branch(&restore_fcsr_and_return, eq, t0, Operand(zero_reg)); 2175 __ lw(v0, MemOperand(sp, 0 * kPointerSize)); 2176 2177 __ bind(&restore_fcsr_and_return); 2178 // Restore FCSR and return. 2179 __ ctc1(a3, FCSR); 2180 2181 __ DropAndRet(argc + 1); 2182 2183 __ bind(&wont_fit_smi); 2184 // Restore FCSR and fall to slow case. 2185 __ ctc1(a3, FCSR); 2186 2187 __ bind(&slow); 2188 // We do not have to patch the receiver because the function makes no use of 2189 // it. 2190 GenerateJumpFunctionIgnoreReceiver(function); 2191 2192 HandlerFrontendFooter(&miss); 2193 2194 // Return the generated code. 2195 return GetCode(type, name); 2196 } 2197 2198 2199 Handle<Code> CallStubCompiler::CompileMathAbsCall( 2200 Handle<Object> object, 2201 Handle<JSObject> holder, 2202 Handle<Cell> cell, 2203 Handle<JSFunction> function, 2204 Handle<String> name, 2205 Code::StubType type) { 2206 const int argc = arguments().immediate(); 2207 // If the object is not a JSObject or we got an unexpected number of 2208 // arguments, bail out to the regular call. 2209 if (!object->IsJSObject() || argc != 1) return Handle<Code>::null(); 2210 2211 Label miss; 2212 2213 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 2214 if (!cell.is_null()) { 2215 ASSERT(cell->value() == *function); 2216 GenerateLoadFunctionFromCell(cell, function, &miss); 2217 } 2218 2219 // Load the (only) argument into v0. 2220 __ lw(v0, MemOperand(sp, 0 * kPointerSize)); 2221 2222 // Check if the argument is a smi. 2223 Label not_smi; 2224 STATIC_ASSERT(kSmiTag == 0); 2225 __ JumpIfNotSmi(v0, ¬_smi); 2226 2227 // Do bitwise not or do nothing depending on the sign of the 2228 // argument. 2229 __ sra(t0, v0, kBitsPerInt - 1); 2230 __ Xor(a1, v0, t0); 2231 2232 // Add 1 or do nothing depending on the sign of the argument. 2233 __ Subu(v0, a1, t0); 2234 2235 // If the result is still negative, go to the slow case. 2236 // This only happens for the most negative smi. 2237 Label slow; 2238 __ Branch(&slow, lt, v0, Operand(zero_reg)); 2239 2240 // Smi case done. 2241 __ DropAndRet(argc + 1); 2242 2243 // Check if the argument is a heap number and load its exponent and 2244 // sign. 2245 __ bind(¬_smi); 2246 __ CheckMap(v0, a1, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK); 2247 __ lw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset)); 2248 2249 // Check the sign of the argument. If the argument is positive, 2250 // just return it. 2251 Label negative_sign; 2252 __ And(t0, a1, Operand(HeapNumber::kSignMask)); 2253 __ Branch(&negative_sign, ne, t0, Operand(zero_reg)); 2254 __ DropAndRet(argc + 1); 2255 2256 // If the argument is negative, clear the sign, and return a new 2257 // number. 2258 __ bind(&negative_sign); 2259 __ Xor(a1, a1, Operand(HeapNumber::kSignMask)); 2260 __ lw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset)); 2261 __ LoadRoot(t2, Heap::kHeapNumberMapRootIndex); 2262 __ AllocateHeapNumber(v0, t0, t1, t2, &slow); 2263 __ sw(a1, FieldMemOperand(v0, HeapNumber::kExponentOffset)); 2264 __ sw(a3, FieldMemOperand(v0, HeapNumber::kMantissaOffset)); 2265 __ DropAndRet(argc + 1); 2266 2267 __ bind(&slow); 2268 // We do not have to patch the receiver because the function makes no use of 2269 // it. 2270 GenerateJumpFunctionIgnoreReceiver(function); 2271 2272 HandlerFrontendFooter(&miss); 2273 2274 // Return the generated code. 2275 return GetCode(type, name); 2276 } 2277 2278 2279 Handle<Code> CallStubCompiler::CompileFastApiCall( 2280 const CallOptimization& optimization, 2281 Handle<Object> object, 2282 Handle<JSObject> holder, 2283 Handle<Cell> cell, 2284 Handle<JSFunction> function, 2285 Handle<String> name) { 2286 2287 Counters* counters = isolate()->counters(); 2288 2289 ASSERT(optimization.is_simple_api_call()); 2290 // Bail out if object is a global object as we don't want to 2291 // repatch it to global receiver. 2292 if (object->IsGlobalObject()) return Handle<Code>::null(); 2293 if (!cell.is_null()) return Handle<Code>::null(); 2294 if (!object->IsJSObject()) return Handle<Code>::null(); 2295 int depth = optimization.GetPrototypeDepthOfExpectedType( 2296 Handle<JSObject>::cast(object), holder); 2297 if (depth == kInvalidProtoDepth) return Handle<Code>::null(); 2298 2299 Label miss, miss_before_stack_reserved; 2300 2301 GenerateNameCheck(name, &miss_before_stack_reserved); 2302 2303 // Get the receiver from the stack. 2304 const int argc = arguments().immediate(); 2305 __ lw(a1, MemOperand(sp, argc * kPointerSize)); 2306 2307 // Check that the receiver isn't a smi. 2308 __ JumpIfSmi(a1, &miss_before_stack_reserved); 2309 2310 __ IncrementCounter(counters->call_const(), 1, a0, a3); 2311 __ IncrementCounter(counters->call_const_fast_api(), 1, a0, a3); 2312 2313 ReserveSpaceForFastApiCall(masm(), a0); 2314 2315 // Check that the maps haven't changed and find a Holder as a side effect. 2316 CheckPrototypes( 2317 IC::CurrentTypeOf(object, isolate()), 2318 a1, holder, a0, a3, t0, name, depth, &miss); 2319 2320 GenerateFastApiDirectCall(masm(), optimization, argc, false); 2321 2322 __ bind(&miss); 2323 FreeSpaceForFastApiCall(masm()); 2324 2325 HandlerFrontendFooter(&miss_before_stack_reserved); 2326 2327 // Return the generated code. 2328 return GetCode(function); 2329 } 2330 2331 2332 void StubCompiler::GenerateBooleanCheck(Register object, Label* miss) { 2333 Label success; 2334 // Check that the object is a boolean. 2335 __ LoadRoot(at, Heap::kTrueValueRootIndex); 2336 __ Branch(&success, eq, object, Operand(at)); 2337 __ LoadRoot(at, Heap::kFalseValueRootIndex); 2338 __ Branch(miss, ne, object, Operand(at)); 2339 __ bind(&success); 2340 } 2341 2342 2343 void CallStubCompiler::PatchGlobalProxy(Handle<Object> object) { 2344 if (object->IsGlobalObject()) { 2345 const int argc = arguments().immediate(); 2346 const int receiver_offset = argc * kPointerSize; 2347 __ lw(a3, FieldMemOperand(a0, GlobalObject::kGlobalReceiverOffset)); 2348 __ sw(a3, MemOperand(sp, receiver_offset)); 2349 } 2350 } 2351 2352 2353 Register CallStubCompiler::HandlerFrontendHeader(Handle<Object> object, 2354 Handle<JSObject> holder, 2355 Handle<Name> name, 2356 CheckType check, 2357 Label* miss) { 2358 // ----------- S t a t e ------------- 2359 // -- a2 : name 2360 // -- ra : return address 2361 // ----------------------------------- 2362 GenerateNameCheck(name, miss); 2363 2364 Register reg = a0; 2365 2366 // Get the receiver from the stack. 2367 const int argc = arguments().immediate(); 2368 const int receiver_offset = argc * kPointerSize; 2369 __ lw(a0, MemOperand(sp, receiver_offset)); 2370 2371 // Check that the receiver isn't a smi. 2372 if (check != NUMBER_CHECK) { 2373 __ JumpIfSmi(a0, miss); 2374 } 2375 2376 // Make sure that it's okay not to patch the on stack receiver 2377 // unless we're doing a receiver map check. 2378 ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); 2379 switch (check) { 2380 case RECEIVER_MAP_CHECK: 2381 __ IncrementCounter(isolate()->counters()->call_const(), 1, a1, a3); 2382 2383 // Check that the maps haven't changed. 2384 reg = CheckPrototypes( 2385 IC::CurrentTypeOf(object, isolate()), 2386 reg, holder, a1, a3, t0, name, miss); 2387 break; 2388 2389 case STRING_CHECK: { 2390 // Check that the object is a string. 2391 __ GetObjectType(reg, a3, a3); 2392 __ Branch(miss, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE)); 2393 // Check that the maps starting from the prototype haven't changed. 2394 GenerateDirectLoadGlobalFunctionPrototype( 2395 masm(), Context::STRING_FUNCTION_INDEX, a1, miss); 2396 break; 2397 } 2398 case SYMBOL_CHECK: { 2399 // Check that the object is a symbol. 2400 __ GetObjectType(reg, a1, a3); 2401 __ Branch(miss, ne, a3, Operand(SYMBOL_TYPE)); 2402 // Check that the maps starting from the prototype haven't changed. 2403 GenerateDirectLoadGlobalFunctionPrototype( 2404 masm(), Context::SYMBOL_FUNCTION_INDEX, a1, miss); 2405 break; 2406 } 2407 case NUMBER_CHECK: { 2408 Label fast; 2409 // Check that the object is a smi or a heap number. 2410 __ JumpIfSmi(reg, &fast); 2411 __ GetObjectType(reg, a3, a3); 2412 __ Branch(miss, ne, a3, Operand(HEAP_NUMBER_TYPE)); 2413 __ bind(&fast); 2414 // Check that the maps starting from the prototype haven't changed. 2415 GenerateDirectLoadGlobalFunctionPrototype( 2416 masm(), Context::NUMBER_FUNCTION_INDEX, a1, miss); 2417 break; 2418 } 2419 case BOOLEAN_CHECK: { 2420 GenerateBooleanCheck(reg, miss); 2421 2422 // Check that the maps starting from the prototype haven't changed. 2423 GenerateDirectLoadGlobalFunctionPrototype( 2424 masm(), Context::BOOLEAN_FUNCTION_INDEX, a1, miss); 2425 break; 2426 } 2427 } 2428 2429 if (check != RECEIVER_MAP_CHECK) { 2430 Handle<Object> prototype(object->GetPrototype(isolate()), isolate()); 2431 reg = CheckPrototypes( 2432 IC::CurrentTypeOf(prototype, isolate()), 2433 a1, holder, a1, a3, t0, name, miss); 2434 } 2435 2436 return reg; 2437 } 2438 2439 2440 void CallStubCompiler::GenerateJumpFunction(Handle<Object> object, 2441 Register function, 2442 Label* miss) { 2443 ASSERT(function.is(a1)); 2444 // Check that the function really is a function. 2445 GenerateFunctionCheck(function, a3, miss); 2446 PatchGlobalProxy(object); 2447 // Invoke the function. 2448 __ InvokeFunction(a1, arguments(), JUMP_FUNCTION, 2449 NullCallWrapper(), call_kind()); 2450 } 2451 2452 2453 Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject> object, 2454 Handle<JSObject> holder, 2455 Handle<Name> name) { 2456 Label miss; 2457 2458 GenerateNameCheck(name, &miss); 2459 2460 // Get the number of arguments. 2461 const int argc = arguments().immediate(); 2462 LookupResult lookup(isolate()); 2463 LookupPostInterceptor(holder, name, &lookup); 2464 2465 // Get the receiver from the stack. 2466 __ lw(a1, MemOperand(sp, argc * kPointerSize)); 2467 2468 CallInterceptorCompiler compiler(this, arguments(), a2, extra_state()); 2469 compiler.Compile(masm(), object, holder, name, &lookup, a1, a3, t0, a0, 2470 &miss); 2471 2472 // Move returned value, the function to call, to a1. 2473 __ mov(a1, v0); 2474 // Restore receiver. 2475 __ lw(a0, MemOperand(sp, argc * kPointerSize)); 2476 2477 GenerateJumpFunction(object, a1, &miss); 2478 2479 HandlerFrontendFooter(&miss); 2480 2481 // Return the generated code. 2482 return GetCode(Code::FAST, name); 2483 } 2484 2485 2486 Handle<Code> CallStubCompiler::CompileCallGlobal( 2487 Handle<JSObject> object, 2488 Handle<GlobalObject> holder, 2489 Handle<PropertyCell> cell, 2490 Handle<JSFunction> function, 2491 Handle<Name> name) { 2492 if (HasCustomCallGenerator(function)) { 2493 Handle<Code> code = CompileCustomCall( 2494 object, holder, cell, function, Handle<String>::cast(name), 2495 Code::NORMAL); 2496 // A null handle means bail out to the regular compiler code below. 2497 if (!code.is_null()) return code; 2498 } 2499 2500 Label miss; 2501 HandlerFrontendHeader(object, holder, name, RECEIVER_MAP_CHECK, &miss); 2502 // Potentially loads a closure that matches the shared function info of the 2503 // function, rather than function. 2504 GenerateLoadFunctionFromCell(cell, function, &miss); 2505 Counters* counters = isolate()->counters(); 2506 __ IncrementCounter(counters->call_global_inline(), 1, a3, t0); 2507 GenerateJumpFunction(object, a1, function); 2508 HandlerFrontendFooter(&miss); 2509 2510 // Return the generated code. 2511 return GetCode(Code::NORMAL, name); 2512 } 2513 2514 2515 Handle<Code> StoreStubCompiler::CompileStoreCallback( 2516 Handle<JSObject> object, 2517 Handle<JSObject> holder, 2518 Handle<Name> name, 2519 Handle<ExecutableAccessorInfo> callback) { 2520 HandlerFrontend(IC::CurrentTypeOf(object, isolate()), 2521 receiver(), holder, name); 2522 2523 // Stub never generated for non-global objects that require access 2524 // checks. 2525 ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded()); 2526 2527 __ push(receiver()); // Receiver. 2528 __ li(at, Operand(callback)); // Callback info. 2529 __ push(at); 2530 __ li(at, Operand(name)); 2531 __ Push(at, value()); 2532 2533 // Do tail-call to the runtime system. 2534 ExternalReference store_callback_property = 2535 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); 2536 __ TailCallExternalReference(store_callback_property, 4, 1); 2537 2538 // Return the generated code. 2539 return GetCode(kind(), Code::FAST, name); 2540 } 2541 2542 2543 Handle<Code> StoreStubCompiler::CompileStoreCallback( 2544 Handle<JSObject> object, 2545 Handle<JSObject> holder, 2546 Handle<Name> name, 2547 const CallOptimization& call_optimization) { 2548 HandlerFrontend(IC::CurrentTypeOf(object, isolate()), 2549 receiver(), holder, name); 2550 2551 Register values[] = { value() }; 2552 GenerateFastApiCall( 2553 masm(), call_optimization, receiver(), scratch3(), 1, values); 2554 2555 // Return the generated code. 2556 return GetCode(kind(), Code::FAST, name); 2557 } 2558 2559 2560 #undef __ 2561 #define __ ACCESS_MASM(masm) 2562 2563 2564 void StoreStubCompiler::GenerateStoreViaSetter( 2565 MacroAssembler* masm, 2566 Handle<JSFunction> setter) { 2567 // ----------- S t a t e ------------- 2568 // -- a0 : value 2569 // -- a1 : receiver 2570 // -- a2 : name 2571 // -- ra : return address 2572 // ----------------------------------- 2573 { 2574 FrameScope scope(masm, StackFrame::INTERNAL); 2575 2576 // Save value register, so we can restore it later. 2577 __ push(a0); 2578 2579 if (!setter.is_null()) { 2580 // Call the JavaScript setter with receiver and value on the stack. 2581 __ push(a1); 2582 __ push(a0); 2583 ParameterCount actual(1); 2584 ParameterCount expected(setter); 2585 __ InvokeFunction(setter, expected, actual, 2586 CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); 2587 } else { 2588 // If we generate a global code snippet for deoptimization only, remember 2589 // the place to continue after deoptimization. 2590 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 2591 } 2592 2593 // We have to return the passed value, not the return value of the setter. 2594 __ pop(v0); 2595 2596 // Restore context register. 2597 __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); 2598 } 2599 __ Ret(); 2600 } 2601 2602 2603 #undef __ 2604 #define __ ACCESS_MASM(masm()) 2605 2606 2607 Handle<Code> StoreStubCompiler::CompileStoreInterceptor( 2608 Handle<JSObject> object, 2609 Handle<Name> name) { 2610 Label miss; 2611 2612 // Check that the map of the object hasn't changed. 2613 __ CheckMap(receiver(), scratch1(), Handle<Map>(object->map()), &miss, 2614 DO_SMI_CHECK); 2615 2616 // Perform global security token check if needed. 2617 if (object->IsJSGlobalProxy()) { 2618 __ CheckAccessGlobalProxy(receiver(), scratch1(), &miss); 2619 } 2620 2621 // Stub is never generated for non-global objects that require access 2622 // checks. 2623 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 2624 2625 __ Push(receiver(), this->name(), value()); 2626 2627 // Do tail-call to the runtime system. 2628 ExternalReference store_ic_property = 2629 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); 2630 __ TailCallExternalReference(store_ic_property, 3, 1); 2631 2632 // Handle store cache miss. 2633 __ bind(&miss); 2634 TailCallBuiltin(masm(), MissBuiltin(kind())); 2635 2636 // Return the generated code. 2637 return GetCode(kind(), Code::FAST, name); 2638 } 2639 2640 2641 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<Type> type, 2642 Handle<JSObject> last, 2643 Handle<Name> name) { 2644 NonexistentHandlerFrontend(type, last, name); 2645 2646 // Return undefined if maps of the full prototype chain is still the same. 2647 __ LoadRoot(v0, Heap::kUndefinedValueRootIndex); 2648 __ Ret(); 2649 2650 // Return the generated code. 2651 return GetCode(kind(), Code::FAST, name); 2652 } 2653 2654 2655 Register* LoadStubCompiler::registers() { 2656 // receiver, name, scratch1, scratch2, scratch3, scratch4. 2657 static Register registers[] = { a0, a2, a3, a1, t0, t1 }; 2658 return registers; 2659 } 2660 2661 2662 Register* KeyedLoadStubCompiler::registers() { 2663 // receiver, name, scratch1, scratch2, scratch3, scratch4. 2664 static Register registers[] = { a1, a0, a2, a3, t0, t1 }; 2665 return registers; 2666 } 2667 2668 2669 Register* StoreStubCompiler::registers() { 2670 // receiver, name, value, scratch1, scratch2, scratch3. 2671 static Register registers[] = { a1, a2, a0, a3, t0, t1 }; 2672 return registers; 2673 } 2674 2675 2676 Register* KeyedStoreStubCompiler::registers() { 2677 // receiver, name, value, scratch1, scratch2, scratch3. 2678 static Register registers[] = { a2, a1, a0, a3, t0, t1 }; 2679 return registers; 2680 } 2681 2682 2683 void KeyedLoadStubCompiler::GenerateNameCheck(Handle<Name> name, 2684 Register name_reg, 2685 Label* miss) { 2686 __ Branch(miss, ne, name_reg, Operand(name)); 2687 } 2688 2689 2690 void KeyedStoreStubCompiler::GenerateNameCheck(Handle<Name> name, 2691 Register name_reg, 2692 Label* miss) { 2693 __ Branch(miss, ne, name_reg, Operand(name)); 2694 } 2695 2696 2697 #undef __ 2698 #define __ ACCESS_MASM(masm) 2699 2700 2701 void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, 2702 Register receiver, 2703 Handle<JSFunction> getter) { 2704 // ----------- S t a t e ------------- 2705 // -- a0 : receiver 2706 // -- a2 : name 2707 // -- ra : return address 2708 // ----------------------------------- 2709 { 2710 FrameScope scope(masm, StackFrame::INTERNAL); 2711 2712 if (!getter.is_null()) { 2713 // Call the JavaScript getter with the receiver on the stack. 2714 __ push(receiver); 2715 ParameterCount actual(0); 2716 ParameterCount expected(getter); 2717 __ InvokeFunction(getter, expected, actual, 2718 CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); 2719 } else { 2720 // If we generate a global code snippet for deoptimization only, remember 2721 // the place to continue after deoptimization. 2722 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 2723 } 2724 2725 // Restore context register. 2726 __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); 2727 } 2728 __ Ret(); 2729 } 2730 2731 2732 #undef __ 2733 #define __ ACCESS_MASM(masm()) 2734 2735 2736 Handle<Code> LoadStubCompiler::CompileLoadGlobal( 2737 Handle<Type> type, 2738 Handle<GlobalObject> global, 2739 Handle<PropertyCell> cell, 2740 Handle<Name> name, 2741 bool is_dont_delete) { 2742 Label miss; 2743 2744 HandlerFrontendHeader(type, receiver(), global, name, &miss); 2745 2746 // Get the value from the cell. 2747 __ li(a3, Operand(cell)); 2748 __ lw(t0, FieldMemOperand(a3, Cell::kValueOffset)); 2749 2750 // Check for deleted property if property can actually be deleted. 2751 if (!is_dont_delete) { 2752 __ LoadRoot(at, Heap::kTheHoleValueRootIndex); 2753 __ Branch(&miss, eq, t0, Operand(at)); 2754 } 2755 2756 HandlerFrontendFooter(name, &miss); 2757 2758 Counters* counters = isolate()->counters(); 2759 __ IncrementCounter(counters->named_load_global_stub(), 1, a1, a3); 2760 __ Ret(USE_DELAY_SLOT); 2761 __ mov(v0, t0); 2762 2763 // Return the generated code. 2764 return GetCode(kind(), Code::NORMAL, name); 2765 } 2766 2767 2768 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( 2769 TypeHandleList* types, 2770 CodeHandleList* handlers, 2771 Handle<Name> name, 2772 Code::StubType type, 2773 IcCheckType check) { 2774 Label miss; 2775 2776 if (check == PROPERTY) { 2777 GenerateNameCheck(name, this->name(), &miss); 2778 } 2779 2780 Label number_case; 2781 Register match = scratch1(); 2782 Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; 2783 __ JumpIfSmi(receiver(), smi_target, match); // Reg match is 0 if Smi. 2784 2785 Register map_reg = scratch2(); 2786 2787 int receiver_count = types->length(); 2788 int number_of_handled_maps = 0; 2789 __ lw(map_reg, FieldMemOperand(receiver(), HeapObject::kMapOffset)); 2790 for (int current = 0; current < receiver_count; ++current) { 2791 Handle<Type> type = types->at(current); 2792 Handle<Map> map = IC::TypeToMap(*type, isolate()); 2793 if (!map->is_deprecated()) { 2794 number_of_handled_maps++; 2795 // Check map and tail call if there's a match. 2796 // Separate compare from branch, to provide path for above JumpIfSmi(). 2797 __ Subu(match, map_reg, Operand(map)); 2798 if (type->Is(Type::Number())) { 2799 ASSERT(!number_case.is_unused()); 2800 __ bind(&number_case); 2801 } 2802 __ Jump(handlers->at(current), RelocInfo::CODE_TARGET, 2803 eq, match, Operand(zero_reg)); 2804 } 2805 } 2806 ASSERT(number_of_handled_maps != 0); 2807 2808 __ bind(&miss); 2809 TailCallBuiltin(masm(), MissBuiltin(kind())); 2810 2811 // Return the generated code. 2812 InlineCacheState state = 2813 number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC; 2814 return GetICCode(kind(), type, name, state); 2815 } 2816 2817 2818 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( 2819 MapHandleList* receiver_maps, 2820 CodeHandleList* handler_stubs, 2821 MapHandleList* transitioned_maps) { 2822 Label miss; 2823 __ JumpIfSmi(receiver(), &miss); 2824 2825 int receiver_count = receiver_maps->length(); 2826 __ lw(scratch1(), FieldMemOperand(receiver(), HeapObject::kMapOffset)); 2827 for (int i = 0; i < receiver_count; ++i) { 2828 if (transitioned_maps->at(i).is_null()) { 2829 __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, eq, 2830 scratch1(), Operand(receiver_maps->at(i))); 2831 } else { 2832 Label next_map; 2833 __ Branch(&next_map, ne, scratch1(), Operand(receiver_maps->at(i))); 2834 __ li(transition_map(), Operand(transitioned_maps->at(i))); 2835 __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET); 2836 __ bind(&next_map); 2837 } 2838 } 2839 2840 __ bind(&miss); 2841 TailCallBuiltin(masm(), MissBuiltin(kind())); 2842 2843 // Return the generated code. 2844 return GetICCode( 2845 kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); 2846 } 2847 2848 2849 #undef __ 2850 #define __ ACCESS_MASM(masm) 2851 2852 2853 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( 2854 MacroAssembler* masm) { 2855 // ---------- S t a t e -------------- 2856 // -- ra : return address 2857 // -- a0 : key 2858 // -- a1 : receiver 2859 // ----------------------------------- 2860 Label slow, miss; 2861 2862 Register key = a0; 2863 Register receiver = a1; 2864 2865 __ JumpIfNotSmi(key, &miss); 2866 __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset)); 2867 __ sra(a2, a0, kSmiTagSize); 2868 __ LoadFromNumberDictionary(&slow, t0, a0, v0, a2, a3, t1); 2869 __ Ret(); 2870 2871 // Slow case, key and receiver still in a0 and a1. 2872 __ bind(&slow); 2873 __ IncrementCounter( 2874 masm->isolate()->counters()->keyed_load_external_array_slow(), 2875 1, a2, a3); 2876 // Entry registers are intact. 2877 // ---------- S t a t e -------------- 2878 // -- ra : return address 2879 // -- a0 : key 2880 // -- a1 : receiver 2881 // ----------------------------------- 2882 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); 2883 2884 // Miss case, call the runtime. 2885 __ bind(&miss); 2886 2887 // ---------- S t a t e -------------- 2888 // -- ra : return address 2889 // -- a0 : key 2890 // -- a1 : receiver 2891 // ----------------------------------- 2892 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); 2893 } 2894 2895 2896 #undef __ 2897 2898 } } // namespace v8::internal 2899 2900 #endif // V8_TARGET_ARCH_MIPS 2901