stub-cache-ia32.cc revision 4515c472dc3e5ed2448a564600976759e569a0a8
1// Copyright 2006-2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#include "ic-inl.h" 31#include "codegen-inl.h" 32#include "stub-cache.h" 33 34namespace v8 { 35namespace internal { 36 37#define __ ACCESS_MASM(masm) 38 39 40static void ProbeTable(MacroAssembler* masm, 41 Code::Flags flags, 42 StubCache::Table table, 43 Register name, 44 Register offset, 45 Register extra) { 46 ExternalReference key_offset(SCTableReference::keyReference(table)); 47 ExternalReference value_offset(SCTableReference::valueReference(table)); 48 49 Label miss; 50 51 if (extra.is_valid()) { 52 // Get the code entry from the cache. 53 __ mov(extra, Operand::StaticArray(offset, times_2, value_offset)); 54 55 // Check that the key in the entry matches the name. 56 __ cmp(name, Operand::StaticArray(offset, times_2, key_offset)); 57 __ j(not_equal, &miss, not_taken); 58 59 // Check that the flags match what we're looking for. 60 __ mov(offset, FieldOperand(extra, Code::kFlagsOffset)); 61 __ and_(offset, ~Code::kFlagsNotUsedInLookup); 62 __ cmp(offset, flags); 63 __ j(not_equal, &miss); 64 65 // Jump to the first instruction in the code stub. 66 __ add(Operand(extra), Immediate(Code::kHeaderSize - kHeapObjectTag)); 67 __ jmp(Operand(extra)); 68 69 __ bind(&miss); 70 } else { 71 // Save the offset on the stack. 72 __ push(offset); 73 74 // Check that the key in the entry matches the name. 75 __ cmp(name, Operand::StaticArray(offset, times_2, key_offset)); 76 __ j(not_equal, &miss, not_taken); 77 78 // Get the code entry from the cache. 79 __ mov(offset, Operand::StaticArray(offset, times_2, value_offset)); 80 81 // Check that the flags match what we're looking for. 82 __ mov(offset, FieldOperand(offset, Code::kFlagsOffset)); 83 __ and_(offset, ~Code::kFlagsNotUsedInLookup); 84 __ cmp(offset, flags); 85 __ j(not_equal, &miss); 86 87 // Restore offset and re-load code entry from cache. 88 __ pop(offset); 89 __ mov(offset, Operand::StaticArray(offset, times_2, value_offset)); 90 91 // Jump to the first instruction in the code stub. 92 __ add(Operand(offset), Immediate(Code::kHeaderSize - kHeapObjectTag)); 93 __ jmp(Operand(offset)); 94 95 // Pop at miss. 96 __ bind(&miss); 97 __ pop(offset); 98 } 99} 100 101 102void StubCache::GenerateProbe(MacroAssembler* masm, 103 Code::Flags flags, 104 Register receiver, 105 Register name, 106 Register scratch, 107 Register extra) { 108 Label miss; 109 110 // Make sure that code is valid. The shifting code relies on the 111 // entry size being 8. 112 ASSERT(sizeof(Entry) == 8); 113 114 // Make sure the flags does not name a specific type. 115 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); 116 117 // Make sure that there are no register conflicts. 118 ASSERT(!scratch.is(receiver)); 119 ASSERT(!scratch.is(name)); 120 ASSERT(!extra.is(receiver)); 121 ASSERT(!extra.is(name)); 122 ASSERT(!extra.is(scratch)); 123 124 // Check that the receiver isn't a smi. 125 __ test(receiver, Immediate(kSmiTagMask)); 126 __ j(zero, &miss, not_taken); 127 128 // Get the map of the receiver and compute the hash. 129 __ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); 130 __ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); 131 __ xor_(scratch, flags); 132 __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); 133 134 // Probe the primary table. 135 ProbeTable(masm, flags, kPrimary, name, scratch, extra); 136 137 // Primary miss: Compute hash for secondary probe. 138 __ mov(scratch, FieldOperand(name, String::kHashFieldOffset)); 139 __ add(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); 140 __ xor_(scratch, flags); 141 __ and_(scratch, (kPrimaryTableSize - 1) << kHeapObjectTagSize); 142 __ sub(scratch, Operand(name)); 143 __ add(Operand(scratch), Immediate(flags)); 144 __ and_(scratch, (kSecondaryTableSize - 1) << kHeapObjectTagSize); 145 146 // Probe the secondary table. 147 ProbeTable(masm, flags, kSecondary, name, scratch, extra); 148 149 // Cache miss: Fall-through and let caller handle the miss by 150 // entering the runtime system. 151 __ bind(&miss); 152} 153 154 155static void PushInterceptorArguments(MacroAssembler* masm, 156 Register receiver, 157 Register holder, 158 Register name, 159 JSObject* holder_obj) { 160 __ push(receiver); 161 __ push(holder); 162 __ push(name); 163 InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); 164 ASSERT(!Heap::InNewSpace(interceptor)); 165 __ mov(receiver, Immediate(Handle<Object>(interceptor))); 166 __ push(receiver); 167 __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset)); 168} 169 170 171void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, 172 int index, 173 Register prototype) { 174 // Load the global or builtins object from the current context. 175 __ mov(prototype, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); 176 // Load the global context from the global or builtins object. 177 __ mov(prototype, 178 FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); 179 // Load the function from the global context. 180 __ mov(prototype, Operand(prototype, Context::SlotOffset(index))); 181 // Load the initial map. The global functions all have initial maps. 182 __ mov(prototype, 183 FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); 184 // Load the prototype from the initial map. 185 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); 186} 187 188 189void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, 190 Register receiver, 191 Register scratch, 192 Label* miss_label) { 193 // Check that the receiver isn't a smi. 194 __ test(receiver, Immediate(kSmiTagMask)); 195 __ j(zero, miss_label, not_taken); 196 197 // Check that the object is a JS array. 198 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch); 199 __ j(not_equal, miss_label, not_taken); 200 201 // Load length directly from the JS array. 202 __ mov(eax, FieldOperand(receiver, JSArray::kLengthOffset)); 203 __ ret(0); 204} 205 206 207// Generate code to check if an object is a string. If the object is 208// a string, the map's instance type is left in the scratch register. 209static void GenerateStringCheck(MacroAssembler* masm, 210 Register receiver, 211 Register scratch, 212 Label* smi, 213 Label* non_string_object) { 214 // Check that the object isn't a smi. 215 __ test(receiver, Immediate(kSmiTagMask)); 216 __ j(zero, smi, not_taken); 217 218 // Check that the object is a string. 219 __ mov(scratch, FieldOperand(receiver, HeapObject::kMapOffset)); 220 __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); 221 ASSERT(kNotStringTag != 0); 222 __ test(scratch, Immediate(kNotStringTag)); 223 __ j(not_zero, non_string_object, not_taken); 224} 225 226 227void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, 228 Register receiver, 229 Register scratch, 230 Label* miss) { 231 Label load_length, check_wrapper; 232 233 // Check if the object is a string leaving the instance type in the 234 // scratch register. 235 GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper); 236 237 // Load length from the string and convert to a smi. 238 __ bind(&load_length); 239 __ mov(eax, FieldOperand(receiver, String::kLengthOffset)); 240 __ SmiTag(eax); 241 __ ret(0); 242 243 // Check if the object is a JSValue wrapper. 244 __ bind(&check_wrapper); 245 __ cmp(scratch, JS_VALUE_TYPE); 246 __ j(not_equal, miss, not_taken); 247 248 // Check if the wrapped value is a string and load the length 249 // directly if it is. 250 __ mov(receiver, FieldOperand(receiver, JSValue::kValueOffset)); 251 GenerateStringCheck(masm, receiver, scratch, miss, miss); 252 __ jmp(&load_length); 253} 254 255 256void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, 257 Register receiver, 258 Register scratch1, 259 Register scratch2, 260 Label* miss_label) { 261 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); 262 __ mov(eax, Operand(scratch1)); 263 __ ret(0); 264} 265 266 267// Load a fast property out of a holder object (src). In-object properties 268// are loaded directly otherwise the property is loaded from the properties 269// fixed array. 270void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, 271 Register dst, Register src, 272 JSObject* holder, int index) { 273 // Adjust for the number of properties stored in the holder. 274 index -= holder->map()->inobject_properties(); 275 if (index < 0) { 276 // Get the property straight out of the holder. 277 int offset = holder->map()->instance_size() + (index * kPointerSize); 278 __ mov(dst, FieldOperand(src, offset)); 279 } else { 280 // Calculate the offset into the properties array. 281 int offset = index * kPointerSize + FixedArray::kHeaderSize; 282 __ mov(dst, FieldOperand(src, JSObject::kPropertiesOffset)); 283 __ mov(dst, FieldOperand(dst, offset)); 284 } 285} 286 287 288static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, 289 Register receiver, 290 Register holder, 291 Register name, 292 JSObject* holder_obj) { 293 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 294 295 ExternalReference ref = 296 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)); 297 __ mov(eax, Immediate(5)); 298 __ mov(ebx, Immediate(ref)); 299 300 CEntryStub stub(1); 301 __ CallStub(&stub); 302} 303 304 305template <class Compiler> 306static void CompileLoadInterceptor(Compiler* compiler, 307 StubCompiler* stub_compiler, 308 MacroAssembler* masm, 309 JSObject* object, 310 JSObject* holder, 311 String* name, 312 LookupResult* lookup, 313 Register receiver, 314 Register scratch1, 315 Register scratch2, 316 Label* miss) { 317 ASSERT(holder->HasNamedInterceptor()); 318 ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); 319 320 // Check that the receiver isn't a smi. 321 __ test(receiver, Immediate(kSmiTagMask)); 322 __ j(zero, miss, not_taken); 323 324 // Check that the maps haven't changed. 325 Register reg = 326 stub_compiler->CheckPrototypes(object, receiver, holder, 327 scratch1, scratch2, name, miss); 328 329 if (lookup->IsValid() && lookup->IsCacheable()) { 330 compiler->CompileCacheable(masm, 331 stub_compiler, 332 receiver, 333 reg, 334 scratch1, 335 scratch2, 336 holder, 337 lookup, 338 name, 339 miss); 340 } else { 341 compiler->CompileRegular(masm, 342 receiver, 343 reg, 344 scratch2, 345 holder, 346 miss); 347 } 348} 349 350 351class LoadInterceptorCompiler BASE_EMBEDDED { 352 public: 353 explicit LoadInterceptorCompiler(Register name) : name_(name) {} 354 355 void CompileCacheable(MacroAssembler* masm, 356 StubCompiler* stub_compiler, 357 Register receiver, 358 Register holder, 359 Register scratch1, 360 Register scratch2, 361 JSObject* holder_obj, 362 LookupResult* lookup, 363 String* name, 364 Label* miss_label) { 365 AccessorInfo* callback = 0; 366 bool optimize = false; 367 // So far the most popular follow ups for interceptor loads are FIELD 368 // and CALLBACKS, so inline only them, other cases may be added 369 // later. 370 if (lookup->type() == FIELD) { 371 optimize = true; 372 } else if (lookup->type() == CALLBACKS) { 373 Object* callback_object = lookup->GetCallbackObject(); 374 if (callback_object->IsAccessorInfo()) { 375 callback = AccessorInfo::cast(callback_object); 376 optimize = callback->getter() != NULL; 377 } 378 } 379 380 if (!optimize) { 381 CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); 382 return; 383 } 384 385 // Note: starting a frame here makes GC aware of pointers pushed below. 386 __ EnterInternalFrame(); 387 388 if (lookup->type() == CALLBACKS) { 389 __ push(receiver); 390 } 391 __ push(holder); 392 __ push(name_); 393 394 CompileCallLoadPropertyWithInterceptor(masm, 395 receiver, 396 holder, 397 name_, 398 holder_obj); 399 400 Label interceptor_failed; 401 __ cmp(eax, Factory::no_interceptor_result_sentinel()); 402 __ j(equal, &interceptor_failed); 403 __ LeaveInternalFrame(); 404 __ ret(0); 405 406 __ bind(&interceptor_failed); 407 __ pop(name_); 408 __ pop(holder); 409 if (lookup->type() == CALLBACKS) { 410 __ pop(receiver); 411 } 412 413 __ LeaveInternalFrame(); 414 415 if (lookup->type() == FIELD) { 416 holder = stub_compiler->CheckPrototypes(holder_obj, holder, 417 lookup->holder(), scratch1, 418 scratch2, 419 name, 420 miss_label); 421 stub_compiler->GenerateFastPropertyLoad(masm, eax, 422 holder, lookup->holder(), 423 lookup->GetFieldIndex()); 424 __ ret(0); 425 } else { 426 ASSERT(lookup->type() == CALLBACKS); 427 ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); 428 ASSERT(callback != NULL); 429 ASSERT(callback->getter() != NULL); 430 431 Label cleanup; 432 __ pop(scratch2); 433 __ push(receiver); 434 __ push(scratch2); 435 436 holder = stub_compiler->CheckPrototypes(holder_obj, holder, 437 lookup->holder(), scratch1, 438 scratch2, 439 name, 440 &cleanup); 441 442 __ pop(scratch2); // save old return address 443 __ push(holder); 444 __ mov(holder, Immediate(Handle<AccessorInfo>(callback))); 445 __ push(holder); 446 __ push(FieldOperand(holder, AccessorInfo::kDataOffset)); 447 __ push(name_); 448 __ push(scratch2); // restore old return address 449 450 ExternalReference ref = 451 ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); 452 __ TailCallRuntime(ref, 5, 1); 453 454 __ bind(&cleanup); 455 __ pop(scratch1); 456 __ pop(scratch2); 457 __ push(scratch1); 458 } 459 } 460 461 462 void CompileRegular(MacroAssembler* masm, 463 Register receiver, 464 Register holder, 465 Register scratch, 466 JSObject* holder_obj, 467 Label* miss_label) { 468 __ pop(scratch); // save old return address 469 PushInterceptorArguments(masm, receiver, holder, name_, holder_obj); 470 __ push(scratch); // restore old return address 471 472 ExternalReference ref = ExternalReference( 473 IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); 474 __ TailCallRuntime(ref, 5, 1); 475 } 476 477 private: 478 Register name_; 479}; 480 481 482class CallInterceptorCompiler BASE_EMBEDDED { 483 public: 484 CallInterceptorCompiler(const ParameterCount& arguments, Register name) 485 : arguments_(arguments), argc_(arguments.immediate()), name_(name) {} 486 487 void CompileCacheable(MacroAssembler* masm, 488 StubCompiler* stub_compiler, 489 Register receiver, 490 Register holder, 491 Register scratch1, 492 Register scratch2, 493 JSObject* holder_obj, 494 LookupResult* lookup, 495 String* name, 496 Label* miss_label) { 497 JSFunction* function = 0; 498 bool optimize = false; 499 // So far the most popular case for failed interceptor is 500 // CONSTANT_FUNCTION sitting below. 501 if (lookup->type() == CONSTANT_FUNCTION) { 502 function = lookup->GetConstantFunction(); 503 // JSArray holder is a special case for call constant function 504 // (see the corresponding code). 505 if (function->is_compiled() && !holder_obj->IsJSArray()) { 506 optimize = true; 507 } 508 } 509 510 if (!optimize) { 511 CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); 512 return; 513 } 514 515 __ EnterInternalFrame(); 516 __ push(holder); // Save the holder. 517 __ push(name_); // Save the name. 518 519 CompileCallLoadPropertyWithInterceptor(masm, 520 receiver, 521 holder, 522 name_, 523 holder_obj); 524 525 __ pop(name_); // Restore the name. 526 __ pop(receiver); // Restore the holder. 527 __ LeaveInternalFrame(); 528 529 __ cmp(eax, Factory::no_interceptor_result_sentinel()); 530 Label invoke; 531 __ j(not_equal, &invoke); 532 533 stub_compiler->CheckPrototypes(holder_obj, receiver, 534 lookup->holder(), scratch1, 535 scratch2, 536 name, 537 miss_label); 538 if (lookup->holder()->IsGlobalObject()) { 539 __ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize)); 540 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 541 __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx); 542 } 543 544 ASSERT(function->is_compiled()); 545 // Get the function and setup the context. 546 __ mov(edi, Immediate(Handle<JSFunction>(function))); 547 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 548 549 // Jump to the cached code (tail call). 550 Handle<Code> code(function->code()); 551 ParameterCount expected(function->shared()->formal_parameter_count()); 552 __ InvokeCode(code, expected, arguments_, 553 RelocInfo::CODE_TARGET, JUMP_FUNCTION); 554 555 __ bind(&invoke); 556 } 557 558 void CompileRegular(MacroAssembler* masm, 559 Register receiver, 560 Register holder, 561 Register scratch, 562 JSObject* holder_obj, 563 Label* miss_label) { 564 __ EnterInternalFrame(); 565 // Save the name_ register across the call. 566 __ push(name_); 567 568 PushInterceptorArguments(masm, 569 receiver, 570 holder, 571 name_, 572 holder_obj); 573 574 ExternalReference ref = ExternalReference( 575 IC_Utility(IC::kLoadPropertyWithInterceptorForCall)); 576 __ mov(eax, Immediate(5)); 577 __ mov(ebx, Immediate(ref)); 578 579 CEntryStub stub(1); 580 __ CallStub(&stub); 581 582 // Restore the name_ register. 583 __ pop(name_); 584 __ LeaveInternalFrame(); 585 } 586 587 private: 588 const ParameterCount& arguments_; 589 int argc_; 590 Register name_; 591}; 592 593 594void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { 595 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); 596 Code* code = NULL; 597 if (kind == Code::LOAD_IC) { 598 code = Builtins::builtin(Builtins::LoadIC_Miss); 599 } else { 600 code = Builtins::builtin(Builtins::KeyedLoadIC_Miss); 601 } 602 603 Handle<Code> ic(code); 604 __ jmp(ic, RelocInfo::CODE_TARGET); 605} 606 607 608void StubCompiler::GenerateStoreField(MacroAssembler* masm, 609 Builtins::Name storage_extend, 610 JSObject* object, 611 int index, 612 Map* transition, 613 Register receiver_reg, 614 Register name_reg, 615 Register scratch, 616 Label* miss_label) { 617 // Check that the object isn't a smi. 618 __ test(receiver_reg, Immediate(kSmiTagMask)); 619 __ j(zero, miss_label, not_taken); 620 621 // Check that the map of the object hasn't changed. 622 __ cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), 623 Immediate(Handle<Map>(object->map()))); 624 __ j(not_equal, miss_label, not_taken); 625 626 // Perform global security token check if needed. 627 if (object->IsJSGlobalProxy()) { 628 __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label); 629 } 630 631 // Stub never generated for non-global objects that require access 632 // checks. 633 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 634 635 // Perform map transition for the receiver if necessary. 636 if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { 637 // The properties must be extended before we can store the value. 638 // We jump to a runtime call that extends the properties array. 639 __ mov(ecx, Immediate(Handle<Map>(transition))); 640 Handle<Code> ic(Builtins::builtin(storage_extend)); 641 __ jmp(ic, RelocInfo::CODE_TARGET); 642 return; 643 } 644 645 if (transition != NULL) { 646 // Update the map of the object; no write barrier updating is 647 // needed because the map is never in new space. 648 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), 649 Immediate(Handle<Map>(transition))); 650 } 651 652 // Adjust for the number of properties stored in the object. Even in the 653 // face of a transition we can use the old map here because the size of the 654 // object and the number of in-object properties is not going to change. 655 index -= object->map()->inobject_properties(); 656 657 if (index < 0) { 658 // Set the property straight into the object. 659 int offset = object->map()->instance_size() + (index * kPointerSize); 660 __ mov(FieldOperand(receiver_reg, offset), eax); 661 662 // Update the write barrier for the array address. 663 // Pass the value being stored in the now unused name_reg. 664 __ mov(name_reg, Operand(eax)); 665 __ RecordWrite(receiver_reg, offset, name_reg, scratch); 666 } else { 667 // Write to the properties array. 668 int offset = index * kPointerSize + FixedArray::kHeaderSize; 669 // Get the properties array (optimistically). 670 __ mov(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 671 __ mov(FieldOperand(scratch, offset), eax); 672 673 // Update the write barrier for the array address. 674 // Pass the value being stored in the now unused name_reg. 675 __ mov(name_reg, Operand(eax)); 676 __ RecordWrite(scratch, offset, name_reg, receiver_reg); 677 } 678 679 // Return the value (register eax). 680 __ ret(0); 681} 682 683 684#undef __ 685#define __ ACCESS_MASM(masm()) 686 687 688Register StubCompiler::CheckPrototypes(JSObject* object, 689 Register object_reg, 690 JSObject* holder, 691 Register holder_reg, 692 Register scratch, 693 String* name, 694 Label* miss) { 695 // Check that the maps haven't changed. 696 Register result = 697 masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); 698 699 // If we've skipped any global objects, it's not enough to verify 700 // that their maps haven't changed. 701 while (object != holder) { 702 if (object->IsGlobalObject()) { 703 GlobalObject* global = GlobalObject::cast(object); 704 Object* probe = global->EnsurePropertyCell(name); 705 if (probe->IsFailure()) { 706 set_failure(Failure::cast(probe)); 707 return result; 708 } 709 JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); 710 ASSERT(cell->value()->IsTheHole()); 711 __ mov(scratch, Immediate(Handle<Object>(cell))); 712 __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), 713 Immediate(Factory::the_hole_value())); 714 __ j(not_equal, miss, not_taken); 715 } 716 object = JSObject::cast(object->GetPrototype()); 717 } 718 719 // Return the register containin the holder. 720 return result; 721} 722 723 724void StubCompiler::GenerateLoadField(JSObject* object, 725 JSObject* holder, 726 Register receiver, 727 Register scratch1, 728 Register scratch2, 729 int index, 730 String* name, 731 Label* miss) { 732 // Check that the receiver isn't a smi. 733 __ test(receiver, Immediate(kSmiTagMask)); 734 __ j(zero, miss, not_taken); 735 736 // Check the prototype chain. 737 Register reg = 738 CheckPrototypes(object, receiver, holder, 739 scratch1, scratch2, name, miss); 740 741 // Get the value from the properties. 742 GenerateFastPropertyLoad(masm(), eax, reg, holder, index); 743 __ ret(0); 744} 745 746 747bool StubCompiler::GenerateLoadCallback(JSObject* object, 748 JSObject* holder, 749 Register receiver, 750 Register name_reg, 751 Register scratch1, 752 Register scratch2, 753 AccessorInfo* callback, 754 String* name, 755 Label* miss, 756 Failure** failure) { 757 // Check that the receiver isn't a smi. 758 __ test(receiver, Immediate(kSmiTagMask)); 759 __ j(zero, miss, not_taken); 760 761 // Check that the maps haven't changed. 762 Register reg = 763 CheckPrototypes(object, receiver, holder, 764 scratch1, scratch2, name, miss); 765 766 Handle<AccessorInfo> callback_handle(callback); 767 768 Register other = reg.is(scratch1) ? scratch2 : scratch1; 769 __ EnterInternalFrame(); 770 __ PushHandleScope(other); 771 // Push the stack address where the list of arguments ends 772 __ mov(other, esp); 773 __ sub(Operand(other), Immediate(2 * kPointerSize)); 774 __ push(other); 775 __ push(receiver); // receiver 776 __ push(reg); // holder 777 __ mov(other, Immediate(callback_handle)); 778 __ push(other); 779 __ push(FieldOperand(other, AccessorInfo::kDataOffset)); // data 780 __ push(name_reg); // name 781 // Save a pointer to where we pushed the arguments pointer. 782 // This will be passed as the const Arguments& to the C++ callback. 783 __ mov(eax, esp); 784 __ add(Operand(eax), Immediate(5 * kPointerSize)); 785 __ mov(ebx, esp); 786 787 // Do call through the api. 788 ASSERT_EQ(6, ApiGetterEntryStub::kStackSpace); 789 Address getter_address = v8::ToCData<Address>(callback->getter()); 790 ApiFunction fun(getter_address); 791 ApiGetterEntryStub stub(callback_handle, &fun); 792 // Emitting a stub call may try to allocate (if the code is not 793 // already generated). Do not allow the assembler to perform a 794 // garbage collection but instead return the allocation failure 795 // object. 796 Object* result = masm()->TryCallStub(&stub); 797 if (result->IsFailure()) { 798 *failure = Failure::cast(result); 799 return false; 800 } 801 802 // We need to avoid using eax since that now holds the result. 803 Register tmp = other.is(eax) ? reg : other; 804 // Emitting PopHandleScope may try to allocate. Do not allow the 805 // assembler to perform a garbage collection but instead return a 806 // failure object. 807 result = masm()->TryPopHandleScope(eax, tmp); 808 if (result->IsFailure()) { 809 *failure = Failure::cast(result); 810 return false; 811 } 812 __ LeaveInternalFrame(); 813 814 __ ret(0); 815 return true; 816} 817 818 819void StubCompiler::GenerateLoadConstant(JSObject* object, 820 JSObject* holder, 821 Register receiver, 822 Register scratch1, 823 Register scratch2, 824 Object* value, 825 String* name, 826 Label* miss) { 827 // Check that the receiver isn't a smi. 828 __ test(receiver, Immediate(kSmiTagMask)); 829 __ j(zero, miss, not_taken); 830 831 // Check that the maps haven't changed. 832 Register reg = 833 CheckPrototypes(object, receiver, holder, 834 scratch1, scratch2, name, miss); 835 836 // Return the constant value. 837 __ mov(eax, Handle<Object>(value)); 838 __ ret(0); 839} 840 841 842void StubCompiler::GenerateLoadInterceptor(JSObject* object, 843 JSObject* holder, 844 LookupResult* lookup, 845 Register receiver, 846 Register name_reg, 847 Register scratch1, 848 Register scratch2, 849 String* name, 850 Label* miss) { 851 LoadInterceptorCompiler compiler(name_reg); 852 CompileLoadInterceptor(&compiler, 853 this, 854 masm(), 855 object, 856 holder, 857 name, 858 lookup, 859 receiver, 860 scratch1, 861 scratch2, 862 miss); 863} 864 865 866// TODO(1241006): Avoid having lazy compile stubs specialized by the 867// number of arguments. It is not needed anymore. 868Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { 869 // Enter an internal frame. 870 __ EnterInternalFrame(); 871 872 // Push a copy of the function onto the stack. 873 __ push(edi); 874 875 __ push(edi); // function is also the parameter to the runtime call 876 __ CallRuntime(Runtime::kLazyCompile, 1); 877 __ pop(edi); 878 879 // Tear down temporary frame. 880 __ LeaveInternalFrame(); 881 882 // Do a tail-call of the compiled function. 883 __ lea(ecx, FieldOperand(eax, Code::kHeaderSize)); 884 __ jmp(Operand(ecx)); 885 886 return GetCodeWithFlags(flags, "LazyCompileStub"); 887} 888 889 890Object* CallStubCompiler::CompileCallField(Object* object, 891 JSObject* holder, 892 int index, 893 String* name) { 894 // ----------- S t a t e ------------- 895 // -- ecx : name 896 // -- esp[0] : return address 897 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 898 // -- ... 899 // -- esp[(argc + 1) * 4] : receiver 900 // ----------------------------------- 901 Label miss; 902 903 // Get the receiver from the stack. 904 const int argc = arguments().immediate(); 905 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 906 907 // Check that the receiver isn't a smi. 908 __ test(edx, Immediate(kSmiTagMask)); 909 __ j(zero, &miss, not_taken); 910 911 // Do the right check and compute the holder register. 912 Register reg = 913 CheckPrototypes(JSObject::cast(object), edx, holder, 914 ebx, eax, name, &miss); 915 916 GenerateFastPropertyLoad(masm(), edi, reg, holder, index); 917 918 // Check that the function really is a function. 919 __ test(edi, Immediate(kSmiTagMask)); 920 __ j(zero, &miss, not_taken); 921 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx); 922 __ j(not_equal, &miss, not_taken); 923 924 // Patch the receiver on the stack with the global proxy if 925 // necessary. 926 if (object->IsGlobalObject()) { 927 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 928 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); 929 } 930 931 // Invoke the function. 932 __ InvokeFunction(edi, arguments(), JUMP_FUNCTION); 933 934 // Handle call cache miss. 935 __ bind(&miss); 936 Handle<Code> ic = ComputeCallMiss(arguments().immediate()); 937 __ jmp(ic, RelocInfo::CODE_TARGET); 938 939 // Return the generated code. 940 return GetCode(FIELD, name); 941} 942 943 944Object* CallStubCompiler::CompileCallConstant(Object* object, 945 JSObject* holder, 946 JSFunction* function, 947 String* name, 948 CheckType check) { 949 // ----------- S t a t e ------------- 950 // -- ecx : name 951 // -- esp[0] : return address 952 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 953 // -- ... 954 // -- esp[(argc + 1) * 4] : receiver 955 // ----------------------------------- 956 Label miss; 957 958 // Get the receiver from the stack. 959 const int argc = arguments().immediate(); 960 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 961 962 // Check that the receiver isn't a smi. 963 if (check != NUMBER_CHECK) { 964 __ test(edx, Immediate(kSmiTagMask)); 965 __ j(zero, &miss, not_taken); 966 } 967 968 // Make sure that it's okay not to patch the on stack receiver 969 // unless we're doing a receiver map check. 970 ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); 971 972 switch (check) { 973 case RECEIVER_MAP_CHECK: 974 // Check that the maps haven't changed. 975 CheckPrototypes(JSObject::cast(object), edx, holder, 976 ebx, eax, name, &miss); 977 978 // Patch the receiver on the stack with the global proxy if 979 // necessary. 980 if (object->IsGlobalObject()) { 981 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 982 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); 983 } 984 break; 985 986 case STRING_CHECK: 987 if (!function->IsBuiltin()) { 988 // Calling non-builtins with a value as receiver requires boxing. 989 __ jmp(&miss); 990 } else { 991 // Check that the object is a string or a symbol. 992 __ mov(eax, FieldOperand(edx, HeapObject::kMapOffset)); 993 __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset)); 994 __ cmp(eax, FIRST_NONSTRING_TYPE); 995 __ j(above_equal, &miss, not_taken); 996 // Check that the maps starting from the prototype haven't changed. 997 GenerateLoadGlobalFunctionPrototype(masm(), 998 Context::STRING_FUNCTION_INDEX, 999 eax); 1000 CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, 1001 ebx, edx, name, &miss); 1002 } 1003 break; 1004 1005 case NUMBER_CHECK: { 1006 if (!function->IsBuiltin()) { 1007 // Calling non-builtins with a value as receiver requires boxing. 1008 __ jmp(&miss); 1009 } else { 1010 Label fast; 1011 // Check that the object is a smi or a heap number. 1012 __ test(edx, Immediate(kSmiTagMask)); 1013 __ j(zero, &fast, taken); 1014 __ CmpObjectType(edx, HEAP_NUMBER_TYPE, eax); 1015 __ j(not_equal, &miss, not_taken); 1016 __ bind(&fast); 1017 // Check that the maps starting from the prototype haven't changed. 1018 GenerateLoadGlobalFunctionPrototype(masm(), 1019 Context::NUMBER_FUNCTION_INDEX, 1020 eax); 1021 CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, 1022 ebx, edx, name, &miss); 1023 } 1024 break; 1025 } 1026 1027 case BOOLEAN_CHECK: { 1028 if (!function->IsBuiltin()) { 1029 // Calling non-builtins with a value as receiver requires boxing. 1030 __ jmp(&miss); 1031 } else { 1032 Label fast; 1033 // Check that the object is a boolean. 1034 __ cmp(edx, Factory::true_value()); 1035 __ j(equal, &fast, taken); 1036 __ cmp(edx, Factory::false_value()); 1037 __ j(not_equal, &miss, not_taken); 1038 __ bind(&fast); 1039 // Check that the maps starting from the prototype haven't changed. 1040 GenerateLoadGlobalFunctionPrototype(masm(), 1041 Context::BOOLEAN_FUNCTION_INDEX, 1042 eax); 1043 CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder, 1044 ebx, edx, name, &miss); 1045 } 1046 break; 1047 } 1048 1049 case JSARRAY_HAS_FAST_ELEMENTS_CHECK: 1050 CheckPrototypes(JSObject::cast(object), edx, holder, 1051 ebx, eax, name, &miss); 1052 // Make sure object->HasFastElements(). 1053 // Get the elements array of the object. 1054 __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); 1055 // Check that the object is in fast mode (not dictionary). 1056 __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), 1057 Immediate(Factory::fixed_array_map())); 1058 __ j(not_equal, &miss, not_taken); 1059 break; 1060 1061 default: 1062 UNREACHABLE(); 1063 } 1064 1065 // Get the function and setup the context. 1066 __ mov(edi, Immediate(Handle<JSFunction>(function))); 1067 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 1068 1069 // Jump to the cached code (tail call). 1070 ASSERT(function->is_compiled()); 1071 Handle<Code> code(function->code()); 1072 ParameterCount expected(function->shared()->formal_parameter_count()); 1073 __ InvokeCode(code, expected, arguments(), 1074 RelocInfo::CODE_TARGET, JUMP_FUNCTION); 1075 1076 // Handle call cache miss. 1077 __ bind(&miss); 1078 Handle<Code> ic = ComputeCallMiss(arguments().immediate()); 1079 __ jmp(ic, RelocInfo::CODE_TARGET); 1080 1081 // Return the generated code. 1082 String* function_name = NULL; 1083 if (function->shared()->name()->IsString()) { 1084 function_name = String::cast(function->shared()->name()); 1085 } 1086 return GetCode(CONSTANT_FUNCTION, function_name); 1087} 1088 1089 1090Object* CallStubCompiler::CompileCallInterceptor(Object* object, 1091 JSObject* holder, 1092 String* name) { 1093 // ----------- S t a t e ------------- 1094 // -- ecx : name 1095 // -- esp[0] : return address 1096 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1097 // -- ... 1098 // -- esp[(argc + 1) * 4] : receiver 1099 // ----------------------------------- 1100 Label miss; 1101 1102 // Get the number of arguments. 1103 const int argc = arguments().immediate(); 1104 1105 LookupResult lookup; 1106 LookupPostInterceptor(holder, name, &lookup); 1107 1108 // Get the receiver from the stack. 1109 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1110 1111 CallInterceptorCompiler compiler(arguments(), ecx); 1112 CompileLoadInterceptor(&compiler, 1113 this, 1114 masm(), 1115 JSObject::cast(object), 1116 holder, 1117 name, 1118 &lookup, 1119 edx, 1120 ebx, 1121 edi, 1122 &miss); 1123 1124 // Restore receiver. 1125 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1126 1127 // Check that the function really is a function. 1128 __ test(eax, Immediate(kSmiTagMask)); 1129 __ j(zero, &miss, not_taken); 1130 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); 1131 __ j(not_equal, &miss, not_taken); 1132 1133 // Patch the receiver on the stack with the global proxy if 1134 // necessary. 1135 if (object->IsGlobalObject()) { 1136 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 1137 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); 1138 } 1139 1140 // Invoke the function. 1141 __ mov(edi, eax); 1142 __ InvokeFunction(edi, arguments(), JUMP_FUNCTION); 1143 1144 // Handle load cache miss. 1145 __ bind(&miss); 1146 Handle<Code> ic = ComputeCallMiss(argc); 1147 __ jmp(ic, RelocInfo::CODE_TARGET); 1148 1149 // Return the generated code. 1150 return GetCode(INTERCEPTOR, name); 1151} 1152 1153 1154Object* CallStubCompiler::CompileCallGlobal(JSObject* object, 1155 GlobalObject* holder, 1156 JSGlobalPropertyCell* cell, 1157 JSFunction* function, 1158 String* name) { 1159 // ----------- S t a t e ------------- 1160 // -- ecx : name 1161 // -- esp[0] : return address 1162 // -- esp[(argc - n) * 4] : arg[n] (zero-based) 1163 // -- ... 1164 // -- esp[(argc + 1) * 4] : receiver 1165 // ----------------------------------- 1166 Label miss; 1167 1168 // Get the number of arguments. 1169 const int argc = arguments().immediate(); 1170 1171 // Get the receiver from the stack. 1172 __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); 1173 1174 // If the object is the holder then we know that it's a global 1175 // object which can only happen for contextual calls. In this case, 1176 // the receiver cannot be a smi. 1177 if (object != holder) { 1178 __ test(edx, Immediate(kSmiTagMask)); 1179 __ j(zero, &miss, not_taken); 1180 } 1181 1182 // Check that the maps haven't changed. 1183 CheckPrototypes(object, edx, holder, ebx, eax, name, &miss); 1184 1185 // Get the value from the cell. 1186 __ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell))); 1187 __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset)); 1188 1189 // Check that the cell contains the same function. 1190 if (Heap::InNewSpace(function)) { 1191 // We can't embed a pointer to a function in new space so we have 1192 // to verify that the shared function info is unchanged. This has 1193 // the nice side effect that multiple closures based on the same 1194 // function can all use this call IC. Before we load through the 1195 // function, we have to verify that it still is a function. 1196 __ test(edi, Immediate(kSmiTagMask)); 1197 __ j(zero, &miss, not_taken); 1198 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx); 1199 __ j(not_equal, &miss, not_taken); 1200 1201 // Check the shared function info. Make sure it hasn't changed. 1202 __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset), 1203 Immediate(Handle<SharedFunctionInfo>(function->shared()))); 1204 __ j(not_equal, &miss, not_taken); 1205 } else { 1206 __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function))); 1207 __ j(not_equal, &miss, not_taken); 1208 } 1209 1210 // Patch the receiver on the stack with the global proxy. 1211 if (object->IsGlobalObject()) { 1212 __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); 1213 __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); 1214 } 1215 1216 // Setup the context (function already in edi). 1217 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 1218 1219 // Jump to the cached code (tail call). 1220 __ IncrementCounter(&Counters::call_global_inline, 1); 1221 ASSERT(function->is_compiled()); 1222 Handle<Code> code(function->code()); 1223 ParameterCount expected(function->shared()->formal_parameter_count()); 1224 __ InvokeCode(code, expected, arguments(), 1225 RelocInfo::CODE_TARGET, JUMP_FUNCTION); 1226 1227 // Handle call cache miss. 1228 __ bind(&miss); 1229 __ IncrementCounter(&Counters::call_global_inline_miss, 1); 1230 Handle<Code> ic = ComputeCallMiss(arguments().immediate()); 1231 __ jmp(ic, RelocInfo::CODE_TARGET); 1232 1233 // Return the generated code. 1234 return GetCode(NORMAL, name); 1235} 1236 1237 1238Object* StoreStubCompiler::CompileStoreField(JSObject* object, 1239 int index, 1240 Map* transition, 1241 String* name) { 1242 // ----------- S t a t e ------------- 1243 // -- eax : value 1244 // -- ecx : name 1245 // -- edx : receiver 1246 // -- esp[0] : return address 1247 // ----------------------------------- 1248 Label miss; 1249 1250 // Generate store field code. Trashes the name register. 1251 GenerateStoreField(masm(), 1252 Builtins::StoreIC_ExtendStorage, 1253 object, 1254 index, 1255 transition, 1256 edx, ecx, ebx, 1257 &miss); 1258 1259 // Handle store cache miss. 1260 __ bind(&miss); 1261 __ mov(ecx, Immediate(Handle<String>(name))); // restore name 1262 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); 1263 __ jmp(ic, RelocInfo::CODE_TARGET); 1264 1265 // Return the generated code. 1266 return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); 1267} 1268 1269 1270Object* StoreStubCompiler::CompileStoreCallback(JSObject* object, 1271 AccessorInfo* callback, 1272 String* name) { 1273 // ----------- S t a t e ------------- 1274 // -- eax : value 1275 // -- ecx : name 1276 // -- edx : receiver 1277 // -- esp[0] : return address 1278 // ----------------------------------- 1279 Label miss; 1280 1281 // Check that the object isn't a smi. 1282 __ test(edx, Immediate(kSmiTagMask)); 1283 __ j(zero, &miss, not_taken); 1284 1285 // Check that the map of the object hasn't changed. 1286 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), 1287 Immediate(Handle<Map>(object->map()))); 1288 __ j(not_equal, &miss, not_taken); 1289 1290 // Perform global security token check if needed. 1291 if (object->IsJSGlobalProxy()) { 1292 __ CheckAccessGlobalProxy(edx, ebx, &miss); 1293 } 1294 1295 // Stub never generated for non-global objects that require access 1296 // checks. 1297 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 1298 1299 __ pop(ebx); // remove the return address 1300 __ push(edx); // receiver 1301 __ push(Immediate(Handle<AccessorInfo>(callback))); // callback info 1302 __ push(ecx); // name 1303 __ push(eax); // value 1304 __ push(ebx); // restore return address 1305 1306 // Do tail-call to the runtime system. 1307 ExternalReference store_callback_property = 1308 ExternalReference(IC_Utility(IC::kStoreCallbackProperty)); 1309 __ TailCallRuntime(store_callback_property, 4, 1); 1310 1311 // Handle store cache miss. 1312 __ bind(&miss); 1313 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); 1314 __ jmp(ic, RelocInfo::CODE_TARGET); 1315 1316 // Return the generated code. 1317 return GetCode(CALLBACKS, name); 1318} 1319 1320 1321Object* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, 1322 String* name) { 1323 // ----------- S t a t e ------------- 1324 // -- eax : value 1325 // -- ecx : name 1326 // -- edx : receiver 1327 // -- esp[0] : return address 1328 // ----------------------------------- 1329 Label miss; 1330 1331 // Check that the object isn't a smi. 1332 __ test(edx, Immediate(kSmiTagMask)); 1333 __ j(zero, &miss, not_taken); 1334 1335 // Check that the map of the object hasn't changed. 1336 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), 1337 Immediate(Handle<Map>(receiver->map()))); 1338 __ j(not_equal, &miss, not_taken); 1339 1340 // Perform global security token check if needed. 1341 if (receiver->IsJSGlobalProxy()) { 1342 __ CheckAccessGlobalProxy(edx, ebx, &miss); 1343 } 1344 1345 // Stub never generated for non-global objects that require access 1346 // checks. 1347 ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); 1348 1349 __ pop(ebx); // remove the return address 1350 __ push(edx); // receiver 1351 __ push(ecx); // name 1352 __ push(eax); // value 1353 __ push(ebx); // restore return address 1354 1355 // Do tail-call to the runtime system. 1356 ExternalReference store_ic_property = 1357 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); 1358 __ TailCallRuntime(store_ic_property, 3, 1); 1359 1360 // Handle store cache miss. 1361 __ bind(&miss); 1362 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); 1363 __ jmp(ic, RelocInfo::CODE_TARGET); 1364 1365 // Return the generated code. 1366 return GetCode(INTERCEPTOR, name); 1367} 1368 1369 1370Object* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, 1371 JSGlobalPropertyCell* cell, 1372 String* name) { 1373 // ----------- S t a t e ------------- 1374 // -- eax : value 1375 // -- ecx : name 1376 // -- edx : receiver 1377 // -- esp[0] : return address 1378 // ----------------------------------- 1379 Label miss; 1380 1381 // Check that the map of the global has not changed. 1382 __ cmp(FieldOperand(edx, HeapObject::kMapOffset), 1383 Immediate(Handle<Map>(object->map()))); 1384 __ j(not_equal, &miss, not_taken); 1385 1386 // Store the value in the cell. 1387 __ mov(ecx, Immediate(Handle<JSGlobalPropertyCell>(cell))); 1388 __ mov(FieldOperand(ecx, JSGlobalPropertyCell::kValueOffset), eax); 1389 1390 // Return the value (register eax). 1391 __ IncrementCounter(&Counters::named_store_global_inline, 1); 1392 __ ret(0); 1393 1394 // Handle store cache miss. 1395 __ bind(&miss); 1396 __ IncrementCounter(&Counters::named_store_global_inline_miss, 1); 1397 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); 1398 __ jmp(ic, RelocInfo::CODE_TARGET); 1399 1400 // Return the generated code. 1401 return GetCode(NORMAL, name); 1402} 1403 1404 1405Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, 1406 int index, 1407 Map* transition, 1408 String* name) { 1409 // ----------- S t a t e ------------- 1410 // -- eax : value 1411 // -- esp[0] : return address 1412 // -- esp[4] : key 1413 // -- esp[8] : receiver 1414 // ----------------------------------- 1415 Label miss; 1416 1417 __ IncrementCounter(&Counters::keyed_store_field, 1); 1418 1419 // Get the name from the stack. 1420 __ mov(ecx, Operand(esp, 1 * kPointerSize)); 1421 // Check that the name has not changed. 1422 __ cmp(Operand(ecx), Immediate(Handle<String>(name))); 1423 __ j(not_equal, &miss, not_taken); 1424 1425 // Get the object from the stack. 1426 __ mov(ebx, Operand(esp, 2 * kPointerSize)); 1427 1428 // Generate store field code. Trashes the name register. 1429 GenerateStoreField(masm(), 1430 Builtins::KeyedStoreIC_ExtendStorage, 1431 object, 1432 index, 1433 transition, 1434 ebx, ecx, edx, 1435 &miss); 1436 1437 // Handle store cache miss. 1438 __ bind(&miss); 1439 __ DecrementCounter(&Counters::keyed_store_field, 1); 1440 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss)); 1441 __ jmp(ic, RelocInfo::CODE_TARGET); 1442 1443 // Return the generated code. 1444 return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); 1445} 1446 1447 1448 1449Object* LoadStubCompiler::CompileLoadField(JSObject* object, 1450 JSObject* holder, 1451 int index, 1452 String* name) { 1453 // ----------- S t a t e ------------- 1454 // -- ecx : name 1455 // -- esp[0] : return address 1456 // -- esp[4] : receiver 1457 // ----------------------------------- 1458 Label miss; 1459 1460 __ mov(eax, Operand(esp, kPointerSize)); 1461 GenerateLoadField(object, holder, eax, ebx, edx, index, name, &miss); 1462 __ bind(&miss); 1463 GenerateLoadMiss(masm(), Code::LOAD_IC); 1464 1465 // Return the generated code. 1466 return GetCode(FIELD, name); 1467} 1468 1469 1470Object* LoadStubCompiler::CompileLoadCallback(String* name, 1471 JSObject* object, 1472 JSObject* holder, 1473 AccessorInfo* callback) { 1474 // ----------- S t a t e ------------- 1475 // -- ecx : name 1476 // -- esp[0] : return address 1477 // -- esp[4] : receiver 1478 // ----------------------------------- 1479 Label miss; 1480 1481 __ mov(eax, Operand(esp, kPointerSize)); 1482 Failure* failure = Failure::InternalError(); 1483 bool success = GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, 1484 callback, name, &miss, &failure); 1485 if (!success) return failure; 1486 1487 __ bind(&miss); 1488 GenerateLoadMiss(masm(), Code::LOAD_IC); 1489 1490 // Return the generated code. 1491 return GetCode(CALLBACKS, name); 1492} 1493 1494 1495Object* LoadStubCompiler::CompileLoadConstant(JSObject* object, 1496 JSObject* holder, 1497 Object* value, 1498 String* name) { 1499 // ----------- S t a t e ------------- 1500 // -- ecx : name 1501 // -- esp[0] : return address 1502 // -- esp[4] : receiver 1503 // ----------------------------------- 1504 Label miss; 1505 1506 __ mov(eax, Operand(esp, kPointerSize)); 1507 GenerateLoadConstant(object, holder, eax, ebx, edx, value, name, &miss); 1508 __ bind(&miss); 1509 GenerateLoadMiss(masm(), Code::LOAD_IC); 1510 1511 // Return the generated code. 1512 return GetCode(CONSTANT_FUNCTION, name); 1513} 1514 1515 1516Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, 1517 JSObject* holder, 1518 String* name) { 1519 // ----------- S t a t e ------------- 1520 // -- ecx : name 1521 // -- esp[0] : return address 1522 // -- esp[4] : receiver 1523 // ----------------------------------- 1524 Label miss; 1525 1526 LookupResult lookup; 1527 LookupPostInterceptor(holder, name, &lookup); 1528 1529 __ mov(eax, Operand(esp, kPointerSize)); 1530 // TODO(368): Compile in the whole chain: all the interceptors in 1531 // prototypes and ultimate answer. 1532 GenerateLoadInterceptor(receiver, 1533 holder, 1534 &lookup, 1535 eax, 1536 ecx, 1537 edx, 1538 ebx, 1539 name, 1540 &miss); 1541 1542 __ bind(&miss); 1543 GenerateLoadMiss(masm(), Code::LOAD_IC); 1544 1545 // Return the generated code. 1546 return GetCode(INTERCEPTOR, name); 1547} 1548 1549 1550Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, 1551 GlobalObject* holder, 1552 JSGlobalPropertyCell* cell, 1553 String* name, 1554 bool is_dont_delete) { 1555 // ----------- S t a t e ------------- 1556 // -- ecx : name 1557 // -- esp[0] : return address 1558 // -- esp[4] : receiver 1559 // ----------------------------------- 1560 Label miss; 1561 1562 // Get the receiver from the stack. 1563 __ mov(eax, Operand(esp, kPointerSize)); 1564 1565 // If the object is the holder then we know that it's a global 1566 // object which can only happen for contextual loads. In this case, 1567 // the receiver cannot be a smi. 1568 if (object != holder) { 1569 __ test(eax, Immediate(kSmiTagMask)); 1570 __ j(zero, &miss, not_taken); 1571 } 1572 1573 // Check that the maps haven't changed. 1574 CheckPrototypes(object, eax, holder, ebx, edx, name, &miss); 1575 1576 // Get the value from the cell. 1577 __ mov(eax, Immediate(Handle<JSGlobalPropertyCell>(cell))); 1578 __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset)); 1579 1580 // Check for deleted property if property can actually be deleted. 1581 if (!is_dont_delete) { 1582 __ cmp(eax, Factory::the_hole_value()); 1583 __ j(equal, &miss, not_taken); 1584 } else if (FLAG_debug_code) { 1585 __ cmp(eax, Factory::the_hole_value()); 1586 __ Check(not_equal, "DontDelete cells can't contain the hole"); 1587 } 1588 1589 __ IncrementCounter(&Counters::named_load_global_inline, 1); 1590 __ ret(0); 1591 1592 __ bind(&miss); 1593 __ IncrementCounter(&Counters::named_load_global_inline_miss, 1); 1594 GenerateLoadMiss(masm(), Code::LOAD_IC); 1595 1596 // Return the generated code. 1597 return GetCode(NORMAL, name); 1598} 1599 1600 1601Object* KeyedLoadStubCompiler::CompileLoadField(String* name, 1602 JSObject* receiver, 1603 JSObject* holder, 1604 int index) { 1605 // ----------- S t a t e ------------- 1606 // -- esp[0] : return address 1607 // -- esp[4] : name 1608 // -- esp[8] : receiver 1609 // ----------------------------------- 1610 Label miss; 1611 1612 __ mov(eax, Operand(esp, kPointerSize)); 1613 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1614 __ IncrementCounter(&Counters::keyed_load_field, 1); 1615 1616 // Check that the name has not changed. 1617 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1618 __ j(not_equal, &miss, not_taken); 1619 1620 GenerateLoadField(receiver, holder, ecx, ebx, edx, index, name, &miss); 1621 1622 __ bind(&miss); 1623 __ DecrementCounter(&Counters::keyed_load_field, 1); 1624 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1625 1626 // Return the generated code. 1627 return GetCode(FIELD, name); 1628} 1629 1630 1631Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, 1632 JSObject* receiver, 1633 JSObject* holder, 1634 AccessorInfo* callback) { 1635 // ----------- S t a t e ------------- 1636 // -- esp[0] : return address 1637 // -- esp[4] : name 1638 // -- esp[8] : receiver 1639 // ----------------------------------- 1640 Label miss; 1641 1642 __ mov(eax, Operand(esp, kPointerSize)); 1643 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1644 __ IncrementCounter(&Counters::keyed_load_callback, 1); 1645 1646 // Check that the name has not changed. 1647 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1648 __ j(not_equal, &miss, not_taken); 1649 1650 Failure* failure = Failure::InternalError(); 1651 bool success = GenerateLoadCallback(receiver, holder, ecx, eax, ebx, edx, 1652 callback, name, &miss, &failure); 1653 if (!success) return failure; 1654 1655 __ bind(&miss); 1656 __ DecrementCounter(&Counters::keyed_load_callback, 1); 1657 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1658 1659 // Return the generated code. 1660 return GetCode(CALLBACKS, name); 1661} 1662 1663 1664Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name, 1665 JSObject* receiver, 1666 JSObject* holder, 1667 Object* value) { 1668 // ----------- S t a t e ------------- 1669 // -- esp[0] : return address 1670 // -- esp[4] : name 1671 // -- esp[8] : receiver 1672 // ----------------------------------- 1673 Label miss; 1674 1675 __ mov(eax, Operand(esp, kPointerSize)); 1676 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1677 __ IncrementCounter(&Counters::keyed_load_constant_function, 1); 1678 1679 // Check that the name has not changed. 1680 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1681 __ j(not_equal, &miss, not_taken); 1682 1683 GenerateLoadConstant(receiver, holder, ecx, ebx, edx, 1684 value, name, &miss); 1685 __ bind(&miss); 1686 __ DecrementCounter(&Counters::keyed_load_constant_function, 1); 1687 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1688 1689 // Return the generated code. 1690 return GetCode(CONSTANT_FUNCTION, name); 1691} 1692 1693 1694Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, 1695 JSObject* holder, 1696 String* name) { 1697 // ----------- S t a t e ------------- 1698 // -- esp[0] : return address 1699 // -- esp[4] : name 1700 // -- esp[8] : receiver 1701 // ----------------------------------- 1702 Label miss; 1703 1704 __ mov(eax, Operand(esp, kPointerSize)); 1705 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1706 __ IncrementCounter(&Counters::keyed_load_interceptor, 1); 1707 1708 // Check that the name has not changed. 1709 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1710 __ j(not_equal, &miss, not_taken); 1711 1712 LookupResult lookup; 1713 LookupPostInterceptor(holder, name, &lookup); 1714 GenerateLoadInterceptor(receiver, 1715 holder, 1716 &lookup, 1717 ecx, 1718 eax, 1719 edx, 1720 ebx, 1721 name, 1722 &miss); 1723 __ bind(&miss); 1724 __ DecrementCounter(&Counters::keyed_load_interceptor, 1); 1725 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1726 1727 // Return the generated code. 1728 return GetCode(INTERCEPTOR, name); 1729} 1730 1731 1732 1733 1734Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { 1735 // ----------- S t a t e ------------- 1736 // -- esp[0] : return address 1737 // -- esp[4] : name 1738 // -- esp[8] : receiver 1739 // ----------------------------------- 1740 Label miss; 1741 1742 __ mov(eax, Operand(esp, kPointerSize)); 1743 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1744 __ IncrementCounter(&Counters::keyed_load_array_length, 1); 1745 1746 // Check that the name has not changed. 1747 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1748 __ j(not_equal, &miss, not_taken); 1749 1750 GenerateLoadArrayLength(masm(), ecx, edx, &miss); 1751 __ bind(&miss); 1752 __ DecrementCounter(&Counters::keyed_load_array_length, 1); 1753 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1754 1755 // Return the generated code. 1756 return GetCode(CALLBACKS, name); 1757} 1758 1759 1760Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { 1761 // ----------- S t a t e ------------- 1762 // -- esp[0] : return address 1763 // -- esp[4] : name 1764 // -- esp[8] : receiver 1765 // ----------------------------------- 1766 Label miss; 1767 1768 __ mov(eax, Operand(esp, kPointerSize)); 1769 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1770 __ IncrementCounter(&Counters::keyed_load_string_length, 1); 1771 1772 // Check that the name has not changed. 1773 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1774 __ j(not_equal, &miss, not_taken); 1775 1776 GenerateLoadStringLength(masm(), ecx, edx, &miss); 1777 __ bind(&miss); 1778 __ DecrementCounter(&Counters::keyed_load_string_length, 1); 1779 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1780 1781 // Return the generated code. 1782 return GetCode(CALLBACKS, name); 1783} 1784 1785 1786Object* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { 1787 // ----------- S t a t e ------------- 1788 // -- esp[0] : return address 1789 // -- esp[4] : name 1790 // -- esp[8] : receiver 1791 // ----------------------------------- 1792 Label miss; 1793 1794 __ mov(eax, Operand(esp, kPointerSize)); 1795 __ mov(ecx, Operand(esp, 2 * kPointerSize)); 1796 __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); 1797 1798 // Check that the name has not changed. 1799 __ cmp(Operand(eax), Immediate(Handle<String>(name))); 1800 __ j(not_equal, &miss, not_taken); 1801 1802 GenerateLoadFunctionPrototype(masm(), ecx, edx, ebx, &miss); 1803 __ bind(&miss); 1804 __ DecrementCounter(&Counters::keyed_load_function_prototype, 1); 1805 GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); 1806 1807 // Return the generated code. 1808 return GetCode(CALLBACKS, name); 1809} 1810 1811 1812// Specialized stub for constructing objects from functions which only have only 1813// simple assignments of the form this.x = ...; in their body. 1814Object* ConstructStubCompiler::CompileConstructStub( 1815 SharedFunctionInfo* shared) { 1816 // ----------- S t a t e ------------- 1817 // -- eax : argc 1818 // -- edi : constructor 1819 // -- esp[0] : return address 1820 // -- esp[4] : last argument 1821 // ----------------------------------- 1822 Label generic_stub_call; 1823#ifdef ENABLE_DEBUGGER_SUPPORT 1824 // Check to see whether there are any break points in the function code. If 1825 // there are jump to the generic constructor stub which calls the actual 1826 // code for the function thereby hitting the break points. 1827 __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); 1828 __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kDebugInfoOffset)); 1829 __ cmp(ebx, Factory::undefined_value()); 1830 __ j(not_equal, &generic_stub_call, not_taken); 1831#endif 1832 1833 // Load the initial map and verify that it is in fact a map. 1834 __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); 1835 // Will both indicate a NULL and a Smi. 1836 __ test(ebx, Immediate(kSmiTagMask)); 1837 __ j(zero, &generic_stub_call); 1838 __ CmpObjectType(ebx, MAP_TYPE, ecx); 1839 __ j(not_equal, &generic_stub_call); 1840 1841#ifdef DEBUG 1842 // Cannot construct functions this way. 1843 // edi: constructor 1844 // ebx: initial map 1845 __ CmpInstanceType(ebx, JS_FUNCTION_TYPE); 1846 __ Assert(not_equal, "Function constructed by construct stub."); 1847#endif 1848 1849 // Now allocate the JSObject on the heap by moving the new space allocation 1850 // top forward. 1851 // edi: constructor 1852 // ebx: initial map 1853 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); 1854 __ shl(ecx, kPointerSizeLog2); 1855 __ AllocateInNewSpace(ecx, 1856 edx, 1857 ecx, 1858 no_reg, 1859 &generic_stub_call, 1860 NO_ALLOCATION_FLAGS); 1861 1862 // Allocated the JSObject, now initialize the fields and add the heap tag. 1863 // ebx: initial map 1864 // edx: JSObject (untagged) 1865 __ mov(Operand(edx, JSObject::kMapOffset), ebx); 1866 __ mov(ebx, Factory::empty_fixed_array()); 1867 __ mov(Operand(edx, JSObject::kPropertiesOffset), ebx); 1868 __ mov(Operand(edx, JSObject::kElementsOffset), ebx); 1869 1870 // Push the allocated object to the stack. This is the object that will be 1871 // returned (after it is tagged). 1872 __ push(edx); 1873 1874 // eax: argc 1875 // edx: JSObject (untagged) 1876 // Load the address of the first in-object property into edx. 1877 __ lea(edx, Operand(edx, JSObject::kHeaderSize)); 1878 // Calculate the location of the first argument. The stack contains the 1879 // allocated object and the return address on top of the argc arguments. 1880 __ lea(ecx, Operand(esp, eax, times_4, 1 * kPointerSize)); 1881 1882 // Use edi for holding undefined which is used in several places below. 1883 __ mov(edi, Factory::undefined_value()); 1884 1885 // eax: argc 1886 // ecx: first argument 1887 // edx: first in-object property of the JSObject 1888 // edi: undefined 1889 // Fill the initialized properties with a constant value or a passed argument 1890 // depending on the this.x = ...; assignment in the function. 1891 for (int i = 0; i < shared->this_property_assignments_count(); i++) { 1892 if (shared->IsThisPropertyAssignmentArgument(i)) { 1893 // Check if the argument assigned to the property is actually passed. 1894 // If argument is not passed the property is set to undefined, 1895 // otherwise find it on the stack. 1896 int arg_number = shared->GetThisPropertyAssignmentArgument(i); 1897 __ mov(ebx, edi); 1898 __ cmp(eax, arg_number); 1899 if (CpuFeatures::IsSupported(CMOV)) { 1900 CpuFeatures::Scope use_cmov(CMOV); 1901 __ cmov(above, ebx, Operand(ecx, arg_number * -kPointerSize)); 1902 } else { 1903 Label not_passed; 1904 __ j(below_equal, ¬_passed); 1905 __ mov(ebx, Operand(ecx, arg_number * -kPointerSize)); 1906 __ bind(¬_passed); 1907 } 1908 // Store value in the property. 1909 __ mov(Operand(edx, i * kPointerSize), ebx); 1910 } else { 1911 // Set the property to the constant value. 1912 Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i)); 1913 __ mov(Operand(edx, i * kPointerSize), Immediate(constant)); 1914 } 1915 } 1916 1917 // Fill the unused in-object property fields with undefined. 1918 for (int i = shared->this_property_assignments_count(); 1919 i < shared->CalculateInObjectProperties(); 1920 i++) { 1921 __ mov(Operand(edx, i * kPointerSize), edi); 1922 } 1923 1924 // Move argc to ebx and retrieve and tag the JSObject to return. 1925 __ mov(ebx, eax); 1926 __ pop(eax); 1927 __ or_(Operand(eax), Immediate(kHeapObjectTag)); 1928 1929 // Remove caller arguments and receiver from the stack and return. 1930 __ pop(ecx); 1931 __ lea(esp, Operand(esp, ebx, times_pointer_size, 1 * kPointerSize)); 1932 __ push(ecx); 1933 __ IncrementCounter(&Counters::constructed_objects, 1); 1934 __ IncrementCounter(&Counters::constructed_objects_stub, 1); 1935 __ ret(0); 1936 1937 // Jump to the generic stub in case the specialized code cannot handle the 1938 // construction. 1939 __ bind(&generic_stub_call); 1940 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); 1941 Handle<Code> generic_construct_stub(code); 1942 __ jmp(generic_construct_stub, RelocInfo::CODE_TARGET); 1943 1944 // Return the generated code. 1945 return GetCode(); 1946} 1947 1948 1949#undef __ 1950 1951} } // namespace v8::internal 1952