1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/v8.h" 6 7 #if V8_TARGET_ARCH_IA32 8 9 #include "src/ic-inl.h" 10 #include "src/codegen.h" 11 #include "src/stub-cache.h" 12 13 namespace v8 { 14 namespace internal { 15 16 #define __ ACCESS_MASM(masm) 17 18 19 static void ProbeTable(Isolate* isolate, 20 MacroAssembler* masm, 21 Code::Flags flags, 22 StubCache::Table table, 23 Register name, 24 Register receiver, 25 // Number of the cache entry pointer-size scaled. 26 Register offset, 27 Register extra) { 28 ExternalReference key_offset(isolate->stub_cache()->key_reference(table)); 29 ExternalReference value_offset(isolate->stub_cache()->value_reference(table)); 30 ExternalReference map_offset(isolate->stub_cache()->map_reference(table)); 31 32 Label miss; 33 34 // Multiply by 3 because there are 3 fields per entry (name, code, map). 35 __ lea(offset, Operand(offset, offset, times_2, 0)); 36 37 if (extra.is_valid()) { 38 // Get the code entry from the cache. 39 __ mov(extra, Operand::StaticArray(offset, times_1, value_offset)); 40 41 // Check that the key in the entry matches the name. 42 __ cmp(name, Operand::StaticArray(offset, times_1, key_offset)); 43 __ j(not_equal, &miss); 44 45 // Check the map matches. 46 __ mov(offset, Operand::StaticArray(offset, times_1, map_offset)); 47 __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset)); 48 __ j(not_equal, &miss); 49 50 // Check that the flags match what we're looking for. 51 __ mov(offset, FieldOperand(extra, Code::kFlagsOffset)); 52 __ and_(offset, ~Code::kFlagsNotUsedInLookup); 53 __ cmp(offset, flags); 54 __ j(not_equal, &miss); 55 56 #ifdef DEBUG 57 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { 58 __ jmp(&miss); 59 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { 60 __ jmp(&miss); 61 } 62 #endif 63 64 // Jump to the first instruction in the code stub. 65 __ add(extra, Immediate(Code::kHeaderSize - kHeapObjectTag)); 66 __ jmp(extra); 67 68 __ bind(&miss); 69 } else { 70 // Save the offset on the stack. 71 __ push(offset); 72 73 // Check that the key in the entry matches the name. 74 __ cmp(name, Operand::StaticArray(offset, times_1, key_offset)); 75 __ j(not_equal, &miss); 76 77 // Check the map matches. 78 __ mov(offset, Operand::StaticArray(offset, times_1, map_offset)); 79 __ cmp(offset, FieldOperand(receiver, HeapObject::kMapOffset)); 80 __ j(not_equal, &miss); 81 82 // Restore offset register. 83 __ mov(offset, Operand(esp, 0)); 84 85 // Get the code entry from the cache. 86 __ mov(offset, Operand::StaticArray(offset, times_1, value_offset)); 87 88 // Check that the flags match what we're looking for. 89 __ mov(offset, FieldOperand(offset, Code::kFlagsOffset)); 90 __ and_(offset, ~Code::kFlagsNotUsedInLookup); 91 __ cmp(offset, flags); 92 __ j(not_equal, &miss); 93 94 #ifdef DEBUG 95 if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { 96 __ jmp(&miss); 97 } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { 98 __ jmp(&miss); 99 } 100 #endif 101 102 // Restore offset and re-load code entry from cache. 103 __ pop(offset); 104 __ mov(offset, Operand::StaticArray(offset, times_1, value_offset)); 105 106 // Jump to the first instruction in the code stub. 107 __ add(offset, Immediate(Code::kHeaderSize - kHeapObjectTag)); 108 __ jmp(offset); 109 110 // Pop at miss. 111 __ bind(&miss); 112 __ pop(offset); 113 } 114 } 115 116 117 void StubCompiler::GenerateDictionaryNegativeLookup(MacroAssembler* masm, 118 Label* miss_label, 119 Register receiver, 120 Handle<Name> name, 121 Register scratch0, 122 Register scratch1) { 123 ASSERT(name->IsUniqueName()); 124 ASSERT(!receiver.is(scratch0)); 125 Counters* counters = masm->isolate()->counters(); 126 __ IncrementCounter(counters->negative_lookups(), 1); 127 __ IncrementCounter(counters->negative_lookups_miss(), 1); 128 129 __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); 130 131 const int kInterceptorOrAccessCheckNeededMask = 132 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 133 134 // Bail out if the receiver has a named interceptor or requires access checks. 135 __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset), 136 kInterceptorOrAccessCheckNeededMask); 137 __ j(not_zero, miss_label); 138 139 // Check that receiver is a JSObject. 140 __ CmpInstanceType(scratch0, FIRST_SPEC_OBJECT_TYPE); 141 __ j(below, miss_label); 142 143 // Load properties array. 144 Register properties = scratch0; 145 __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); 146 147 // Check that the properties array is a dictionary. 148 __ cmp(FieldOperand(properties, HeapObject::kMapOffset), 149 Immediate(masm->isolate()->factory()->hash_table_map())); 150 __ j(not_equal, miss_label); 151 152 Label done; 153 NameDictionaryLookupStub::GenerateNegativeLookup(masm, 154 miss_label, 155 &done, 156 properties, 157 name, 158 scratch1); 159 __ bind(&done); 160 __ DecrementCounter(counters->negative_lookups_miss(), 1); 161 } 162 163 164 void StubCache::GenerateProbe(MacroAssembler* masm, 165 Code::Flags flags, 166 Register receiver, 167 Register name, 168 Register scratch, 169 Register extra, 170 Register extra2, 171 Register extra3) { 172 Label miss; 173 174 // Assert that code is valid. The multiplying code relies on the entry size 175 // being 12. 176 ASSERT(sizeof(Entry) == 12); 177 178 // Assert the flags do not name a specific type. 179 ASSERT(Code::ExtractTypeFromFlags(flags) == 0); 180 181 // Assert that there are no register conflicts. 182 ASSERT(!scratch.is(receiver)); 183 ASSERT(!scratch.is(name)); 184 ASSERT(!extra.is(receiver)); 185 ASSERT(!extra.is(name)); 186 ASSERT(!extra.is(scratch)); 187 188 // Assert scratch and extra registers are valid, and extra2/3 are unused. 189 ASSERT(!scratch.is(no_reg)); 190 ASSERT(extra2.is(no_reg)); 191 ASSERT(extra3.is(no_reg)); 192 193 Register offset = scratch; 194 scratch = no_reg; 195 196 Counters* counters = masm->isolate()->counters(); 197 __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1); 198 199 // Check that the receiver isn't a smi. 200 __ JumpIfSmi(receiver, &miss); 201 202 // Get the map of the receiver and compute the hash. 203 __ mov(offset, FieldOperand(name, Name::kHashFieldOffset)); 204 __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset)); 205 __ xor_(offset, flags); 206 // We mask out the last two bits because they are not part of the hash and 207 // they are always 01 for maps. Also in the two 'and' instructions below. 208 __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize); 209 // ProbeTable expects the offset to be pointer scaled, which it is, because 210 // the heap object tag size is 2 and the pointer size log 2 is also 2. 211 ASSERT(kHeapObjectTagSize == kPointerSizeLog2); 212 213 // Probe the primary table. 214 ProbeTable(isolate(), masm, flags, kPrimary, name, receiver, offset, extra); 215 216 // Primary miss: Compute hash for secondary probe. 217 __ mov(offset, FieldOperand(name, Name::kHashFieldOffset)); 218 __ add(offset, FieldOperand(receiver, HeapObject::kMapOffset)); 219 __ xor_(offset, flags); 220 __ and_(offset, (kPrimaryTableSize - 1) << kHeapObjectTagSize); 221 __ sub(offset, name); 222 __ add(offset, Immediate(flags)); 223 __ and_(offset, (kSecondaryTableSize - 1) << kHeapObjectTagSize); 224 225 // Probe the secondary table. 226 ProbeTable( 227 isolate(), masm, flags, kSecondary, name, receiver, offset, extra); 228 229 // Cache miss: Fall-through and let caller handle the miss by 230 // entering the runtime system. 231 __ bind(&miss); 232 __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1); 233 } 234 235 236 void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, 237 int index, 238 Register prototype) { 239 __ LoadGlobalFunction(index, prototype); 240 __ LoadGlobalFunctionInitialMap(prototype, prototype); 241 // Load the prototype from the initial map. 242 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); 243 } 244 245 246 void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( 247 MacroAssembler* masm, 248 int index, 249 Register prototype, 250 Label* miss) { 251 // Get the global function with the given index. 252 Handle<JSFunction> function( 253 JSFunction::cast(masm->isolate()->native_context()->get(index))); 254 // Check we're still in the same context. 255 Register scratch = prototype; 256 const int offset = Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX); 257 __ mov(scratch, Operand(esi, offset)); 258 __ mov(scratch, FieldOperand(scratch, GlobalObject::kNativeContextOffset)); 259 __ cmp(Operand(scratch, Context::SlotOffset(index)), function); 260 __ j(not_equal, miss); 261 262 // Load its initial map. The global functions all have initial maps. 263 __ Move(prototype, Immediate(Handle<Map>(function->initial_map()))); 264 // Load the prototype from the initial map. 265 __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); 266 } 267 268 269 void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, 270 Register receiver, 271 Register scratch, 272 Label* miss_label) { 273 // Check that the receiver isn't a smi. 274 __ JumpIfSmi(receiver, miss_label); 275 276 // Check that the object is a JS array. 277 __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch); 278 __ j(not_equal, miss_label); 279 280 // Load length directly from the JS array. 281 __ mov(eax, FieldOperand(receiver, JSArray::kLengthOffset)); 282 __ ret(0); 283 } 284 285 286 void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, 287 Register receiver, 288 Register scratch1, 289 Register scratch2, 290 Label* miss_label) { 291 __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label); 292 __ mov(eax, scratch1); 293 __ ret(0); 294 } 295 296 297 void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, 298 Register dst, 299 Register src, 300 bool inobject, 301 int index, 302 Representation representation) { 303 ASSERT(!representation.IsDouble()); 304 int offset = index * kPointerSize; 305 if (!inobject) { 306 // Calculate the offset into the properties array. 307 offset = offset + FixedArray::kHeaderSize; 308 __ mov(dst, FieldOperand(src, JSObject::kPropertiesOffset)); 309 src = dst; 310 } 311 __ mov(dst, FieldOperand(src, offset)); 312 } 313 314 315 static void PushInterceptorArguments(MacroAssembler* masm, 316 Register receiver, 317 Register holder, 318 Register name, 319 Handle<JSObject> holder_obj) { 320 STATIC_ASSERT(StubCache::kInterceptorArgsNameIndex == 0); 321 STATIC_ASSERT(StubCache::kInterceptorArgsInfoIndex == 1); 322 STATIC_ASSERT(StubCache::kInterceptorArgsThisIndex == 2); 323 STATIC_ASSERT(StubCache::kInterceptorArgsHolderIndex == 3); 324 STATIC_ASSERT(StubCache::kInterceptorArgsLength == 4); 325 __ push(name); 326 Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor()); 327 ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor)); 328 Register scratch = name; 329 __ mov(scratch, Immediate(interceptor)); 330 __ push(scratch); 331 __ push(receiver); 332 __ push(holder); 333 } 334 335 336 static void CompileCallLoadPropertyWithInterceptor( 337 MacroAssembler* masm, 338 Register receiver, 339 Register holder, 340 Register name, 341 Handle<JSObject> holder_obj, 342 IC::UtilityId id) { 343 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 344 __ CallExternalReference( 345 ExternalReference(IC_Utility(id), masm->isolate()), 346 StubCache::kInterceptorArgsLength); 347 } 348 349 350 // Generate call to api function. 351 // This function uses push() to generate smaller, faster code than 352 // the version above. It is an optimization that should will be removed 353 // when api call ICs are generated in hydrogen. 354 void StubCompiler::GenerateFastApiCall(MacroAssembler* masm, 355 const CallOptimization& optimization, 356 Handle<Map> receiver_map, 357 Register receiver, 358 Register scratch_in, 359 bool is_store, 360 int argc, 361 Register* values) { 362 // Copy return value. 363 __ pop(scratch_in); 364 // receiver 365 __ push(receiver); 366 // Write the arguments to stack frame. 367 for (int i = 0; i < argc; i++) { 368 Register arg = values[argc-1-i]; 369 ASSERT(!receiver.is(arg)); 370 ASSERT(!scratch_in.is(arg)); 371 __ push(arg); 372 } 373 __ push(scratch_in); 374 // Stack now matches JSFunction abi. 375 ASSERT(optimization.is_simple_api_call()); 376 377 // Abi for CallApiFunctionStub. 378 Register callee = eax; 379 Register call_data = ebx; 380 Register holder = ecx; 381 Register api_function_address = edx; 382 Register scratch = edi; // scratch_in is no longer valid. 383 384 // Put holder in place. 385 CallOptimization::HolderLookup holder_lookup; 386 Handle<JSObject> api_holder = optimization.LookupHolderOfExpectedType( 387 receiver_map, 388 &holder_lookup); 389 switch (holder_lookup) { 390 case CallOptimization::kHolderIsReceiver: 391 __ Move(holder, receiver); 392 break; 393 case CallOptimization::kHolderFound: 394 __ LoadHeapObject(holder, api_holder); 395 break; 396 case CallOptimization::kHolderNotFound: 397 UNREACHABLE(); 398 break; 399 } 400 401 Isolate* isolate = masm->isolate(); 402 Handle<JSFunction> function = optimization.constant_function(); 403 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 404 Handle<Object> call_data_obj(api_call_info->data(), isolate); 405 406 // Put callee in place. 407 __ LoadHeapObject(callee, function); 408 409 bool call_data_undefined = false; 410 // Put call_data in place. 411 if (isolate->heap()->InNewSpace(*call_data_obj)) { 412 __ mov(scratch, api_call_info); 413 __ mov(call_data, FieldOperand(scratch, CallHandlerInfo::kDataOffset)); 414 } else if (call_data_obj->IsUndefined()) { 415 call_data_undefined = true; 416 __ mov(call_data, Immediate(isolate->factory()->undefined_value())); 417 } else { 418 __ mov(call_data, call_data_obj); 419 } 420 421 // Put api_function_address in place. 422 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 423 __ mov(api_function_address, Immediate(function_address)); 424 425 // Jump to stub. 426 CallApiFunctionStub stub(isolate, is_store, call_data_undefined, argc); 427 __ TailCallStub(&stub); 428 } 429 430 431 void StoreStubCompiler::GenerateRestoreName(MacroAssembler* masm, 432 Label* label, 433 Handle<Name> name) { 434 if (!label->is_unused()) { 435 __ bind(label); 436 __ mov(this->name(), Immediate(name)); 437 } 438 } 439 440 441 // Generate code to check that a global property cell is empty. Create 442 // the property cell at compilation time if no cell exists for the 443 // property. 444 void StubCompiler::GenerateCheckPropertyCell(MacroAssembler* masm, 445 Handle<JSGlobalObject> global, 446 Handle<Name> name, 447 Register scratch, 448 Label* miss) { 449 Handle<PropertyCell> cell = 450 JSGlobalObject::EnsurePropertyCell(global, name); 451 ASSERT(cell->value()->IsTheHole()); 452 Handle<Oddball> the_hole = masm->isolate()->factory()->the_hole_value(); 453 if (masm->serializer_enabled()) { 454 __ mov(scratch, Immediate(cell)); 455 __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset), 456 Immediate(the_hole)); 457 } else { 458 __ cmp(Operand::ForCell(cell), Immediate(the_hole)); 459 } 460 __ j(not_equal, miss); 461 } 462 463 464 void StoreStubCompiler::GenerateNegativeHolderLookup( 465 MacroAssembler* masm, 466 Handle<JSObject> holder, 467 Register holder_reg, 468 Handle<Name> name, 469 Label* miss) { 470 if (holder->IsJSGlobalObject()) { 471 GenerateCheckPropertyCell( 472 masm, Handle<JSGlobalObject>::cast(holder), name, scratch1(), miss); 473 } else if (!holder->HasFastProperties() && !holder->IsJSGlobalProxy()) { 474 GenerateDictionaryNegativeLookup( 475 masm, miss, holder_reg, name, scratch1(), scratch2()); 476 } 477 } 478 479 480 // Receiver_reg is preserved on jumps to miss_label, but may be destroyed if 481 // store is successful. 482 void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm, 483 Handle<JSObject> object, 484 LookupResult* lookup, 485 Handle<Map> transition, 486 Handle<Name> name, 487 Register receiver_reg, 488 Register storage_reg, 489 Register value_reg, 490 Register scratch1, 491 Register scratch2, 492 Register unused, 493 Label* miss_label, 494 Label* slow) { 495 int descriptor = transition->LastAdded(); 496 DescriptorArray* descriptors = transition->instance_descriptors(); 497 PropertyDetails details = descriptors->GetDetails(descriptor); 498 Representation representation = details.representation(); 499 ASSERT(!representation.IsNone()); 500 501 if (details.type() == CONSTANT) { 502 Handle<Object> constant(descriptors->GetValue(descriptor), masm->isolate()); 503 __ CmpObject(value_reg, constant); 504 __ j(not_equal, miss_label); 505 } else if (representation.IsSmi()) { 506 __ JumpIfNotSmi(value_reg, miss_label); 507 } else if (representation.IsHeapObject()) { 508 __ JumpIfSmi(value_reg, miss_label); 509 HeapType* field_type = descriptors->GetFieldType(descriptor); 510 HeapType::Iterator<Map> it = field_type->Classes(); 511 if (!it.Done()) { 512 Label do_store; 513 while (true) { 514 __ CompareMap(value_reg, it.Current()); 515 it.Advance(); 516 if (it.Done()) { 517 __ j(not_equal, miss_label); 518 break; 519 } 520 __ j(equal, &do_store, Label::kNear); 521 } 522 __ bind(&do_store); 523 } 524 } else if (representation.IsDouble()) { 525 Label do_store, heap_number; 526 __ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow); 527 528 __ JumpIfNotSmi(value_reg, &heap_number); 529 __ SmiUntag(value_reg); 530 __ Cvtsi2sd(xmm0, value_reg); 531 __ SmiTag(value_reg); 532 __ jmp(&do_store); 533 534 __ bind(&heap_number); 535 __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(), 536 miss_label, DONT_DO_SMI_CHECK); 537 __ movsd(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset)); 538 539 __ bind(&do_store); 540 __ movsd(FieldOperand(storage_reg, HeapNumber::kValueOffset), xmm0); 541 } 542 543 // Stub never generated for non-global objects that require access 544 // checks. 545 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 546 547 // Perform map transition for the receiver if necessary. 548 if (details.type() == FIELD && 549 object->map()->unused_property_fields() == 0) { 550 // The properties must be extended before we can store the value. 551 // We jump to a runtime call that extends the properties array. 552 __ pop(scratch1); // Return address. 553 __ push(receiver_reg); 554 __ push(Immediate(transition)); 555 __ push(value_reg); 556 __ push(scratch1); 557 __ TailCallExternalReference( 558 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), 559 masm->isolate()), 560 3, 561 1); 562 return; 563 } 564 565 // Update the map of the object. 566 __ mov(scratch1, Immediate(transition)); 567 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1); 568 569 // Update the write barrier for the map field. 570 __ RecordWriteField(receiver_reg, 571 HeapObject::kMapOffset, 572 scratch1, 573 scratch2, 574 kDontSaveFPRegs, 575 OMIT_REMEMBERED_SET, 576 OMIT_SMI_CHECK); 577 578 if (details.type() == CONSTANT) { 579 ASSERT(value_reg.is(eax)); 580 __ ret(0); 581 return; 582 } 583 584 int index = transition->instance_descriptors()->GetFieldIndex( 585 transition->LastAdded()); 586 587 // Adjust for the number of properties stored in the object. Even in the 588 // face of a transition we can use the old map here because the size of the 589 // object and the number of in-object properties is not going to change. 590 index -= object->map()->inobject_properties(); 591 592 SmiCheck smi_check = representation.IsTagged() 593 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 594 // TODO(verwaest): Share this code as a code stub. 595 if (index < 0) { 596 // Set the property straight into the object. 597 int offset = object->map()->instance_size() + (index * kPointerSize); 598 if (representation.IsDouble()) { 599 __ mov(FieldOperand(receiver_reg, offset), storage_reg); 600 } else { 601 __ mov(FieldOperand(receiver_reg, offset), value_reg); 602 } 603 604 if (!representation.IsSmi()) { 605 // Update the write barrier for the array address. 606 if (!representation.IsDouble()) { 607 __ mov(storage_reg, value_reg); 608 } 609 __ RecordWriteField(receiver_reg, 610 offset, 611 storage_reg, 612 scratch1, 613 kDontSaveFPRegs, 614 EMIT_REMEMBERED_SET, 615 smi_check); 616 } 617 } else { 618 // Write to the properties array. 619 int offset = index * kPointerSize + FixedArray::kHeaderSize; 620 // Get the properties array (optimistically). 621 __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 622 if (representation.IsDouble()) { 623 __ mov(FieldOperand(scratch1, offset), storage_reg); 624 } else { 625 __ mov(FieldOperand(scratch1, offset), value_reg); 626 } 627 628 if (!representation.IsSmi()) { 629 // Update the write barrier for the array address. 630 if (!representation.IsDouble()) { 631 __ mov(storage_reg, value_reg); 632 } 633 __ RecordWriteField(scratch1, 634 offset, 635 storage_reg, 636 receiver_reg, 637 kDontSaveFPRegs, 638 EMIT_REMEMBERED_SET, 639 smi_check); 640 } 641 } 642 643 // Return the value (register eax). 644 ASSERT(value_reg.is(eax)); 645 __ ret(0); 646 } 647 648 649 // Both name_reg and receiver_reg are preserved on jumps to miss_label, 650 // but may be destroyed if store is successful. 651 void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, 652 Handle<JSObject> object, 653 LookupResult* lookup, 654 Register receiver_reg, 655 Register name_reg, 656 Register value_reg, 657 Register scratch1, 658 Register scratch2, 659 Label* miss_label) { 660 // Stub never generated for non-global objects that require access 661 // checks. 662 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 663 664 FieldIndex index = lookup->GetFieldIndex(); 665 666 Representation representation = lookup->representation(); 667 ASSERT(!representation.IsNone()); 668 if (representation.IsSmi()) { 669 __ JumpIfNotSmi(value_reg, miss_label); 670 } else if (representation.IsHeapObject()) { 671 __ JumpIfSmi(value_reg, miss_label); 672 HeapType* field_type = lookup->GetFieldType(); 673 HeapType::Iterator<Map> it = field_type->Classes(); 674 if (!it.Done()) { 675 Label do_store; 676 while (true) { 677 __ CompareMap(value_reg, it.Current()); 678 it.Advance(); 679 if (it.Done()) { 680 __ j(not_equal, miss_label); 681 break; 682 } 683 __ j(equal, &do_store, Label::kNear); 684 } 685 __ bind(&do_store); 686 } 687 } else if (representation.IsDouble()) { 688 // Load the double storage. 689 if (index.is_inobject()) { 690 __ mov(scratch1, FieldOperand(receiver_reg, index.offset())); 691 } else { 692 __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 693 __ mov(scratch1, FieldOperand(scratch1, index.offset())); 694 } 695 696 // Store the value into the storage. 697 Label do_store, heap_number; 698 __ JumpIfNotSmi(value_reg, &heap_number); 699 __ SmiUntag(value_reg); 700 __ Cvtsi2sd(xmm0, value_reg); 701 __ SmiTag(value_reg); 702 __ jmp(&do_store); 703 __ bind(&heap_number); 704 __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(), 705 miss_label, DONT_DO_SMI_CHECK); 706 __ movsd(xmm0, FieldOperand(value_reg, HeapNumber::kValueOffset)); 707 __ bind(&do_store); 708 __ movsd(FieldOperand(scratch1, HeapNumber::kValueOffset), xmm0); 709 // Return the value (register eax). 710 ASSERT(value_reg.is(eax)); 711 __ ret(0); 712 return; 713 } 714 715 ASSERT(!representation.IsDouble()); 716 // TODO(verwaest): Share this code as a code stub. 717 SmiCheck smi_check = representation.IsTagged() 718 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 719 if (index.is_inobject()) { 720 // Set the property straight into the object. 721 __ mov(FieldOperand(receiver_reg, index.offset()), value_reg); 722 723 if (!representation.IsSmi()) { 724 // Update the write barrier for the array address. 725 // Pass the value being stored in the now unused name_reg. 726 __ mov(name_reg, value_reg); 727 __ RecordWriteField(receiver_reg, 728 index.offset(), 729 name_reg, 730 scratch1, 731 kDontSaveFPRegs, 732 EMIT_REMEMBERED_SET, 733 smi_check); 734 } 735 } else { 736 // Write to the properties array. 737 // Get the properties array (optimistically). 738 __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 739 __ mov(FieldOperand(scratch1, index.offset()), value_reg); 740 741 if (!representation.IsSmi()) { 742 // Update the write barrier for the array address. 743 // Pass the value being stored in the now unused name_reg. 744 __ mov(name_reg, value_reg); 745 __ RecordWriteField(scratch1, 746 index.offset(), 747 name_reg, 748 receiver_reg, 749 kDontSaveFPRegs, 750 EMIT_REMEMBERED_SET, 751 smi_check); 752 } 753 } 754 755 // Return the value (register eax). 756 ASSERT(value_reg.is(eax)); 757 __ ret(0); 758 } 759 760 761 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { 762 __ jmp(code, RelocInfo::CODE_TARGET); 763 } 764 765 766 #undef __ 767 #define __ ACCESS_MASM(masm()) 768 769 770 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, 771 Register object_reg, 772 Handle<JSObject> holder, 773 Register holder_reg, 774 Register scratch1, 775 Register scratch2, 776 Handle<Name> name, 777 Label* miss, 778 PrototypeCheckType check) { 779 Handle<Map> receiver_map(IC::TypeToMap(*type, isolate())); 780 781 // Make sure there's no overlap between holder and object registers. 782 ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 783 ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) 784 && !scratch2.is(scratch1)); 785 786 // Keep track of the current object in register reg. 787 Register reg = object_reg; 788 int depth = 0; 789 790 Handle<JSObject> current = Handle<JSObject>::null(); 791 if (type->IsConstant()) current = 792 Handle<JSObject>::cast(type->AsConstant()->Value()); 793 Handle<JSObject> prototype = Handle<JSObject>::null(); 794 Handle<Map> current_map = receiver_map; 795 Handle<Map> holder_map(holder->map()); 796 // Traverse the prototype chain and check the maps in the prototype chain for 797 // fast and global objects or do negative lookup for normal objects. 798 while (!current_map.is_identical_to(holder_map)) { 799 ++depth; 800 801 // Only global objects and objects that do not require access 802 // checks are allowed in stubs. 803 ASSERT(current_map->IsJSGlobalProxyMap() || 804 !current_map->is_access_check_needed()); 805 806 prototype = handle(JSObject::cast(current_map->prototype())); 807 if (current_map->is_dictionary_map() && 808 !current_map->IsJSGlobalObjectMap() && 809 !current_map->IsJSGlobalProxyMap()) { 810 if (!name->IsUniqueName()) { 811 ASSERT(name->IsString()); 812 name = factory()->InternalizeString(Handle<String>::cast(name)); 813 } 814 ASSERT(current.is_null() || 815 current->property_dictionary()->FindEntry(name) == 816 NameDictionary::kNotFound); 817 818 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, 819 scratch1, scratch2); 820 821 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 822 reg = holder_reg; // From now on the object will be in holder_reg. 823 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 824 } else { 825 bool in_new_space = heap()->InNewSpace(*prototype); 826 if (depth != 1 || check == CHECK_ALL_MAPS) { 827 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 828 } 829 830 // Check access rights to the global object. This has to happen after 831 // the map check so that we know that the object is actually a global 832 // object. 833 if (current_map->IsJSGlobalProxyMap()) { 834 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 835 } else if (current_map->IsJSGlobalObjectMap()) { 836 GenerateCheckPropertyCell( 837 masm(), Handle<JSGlobalObject>::cast(current), name, 838 scratch2, miss); 839 } 840 841 if (in_new_space) { 842 // Save the map in scratch1 for later. 843 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 844 } 845 846 reg = holder_reg; // From now on the object will be in holder_reg. 847 848 if (in_new_space) { 849 // The prototype is in new space; we cannot store a reference to it 850 // in the code. Load it from the map. 851 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 852 } else { 853 // The prototype is in old space; load it directly. 854 __ mov(reg, prototype); 855 } 856 } 857 858 // Go to the next object in the prototype chain. 859 current = prototype; 860 current_map = handle(current->map()); 861 } 862 863 // Log the check depth. 864 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 865 866 if (depth != 0 || check == CHECK_ALL_MAPS) { 867 // Check the holder map. 868 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 869 } 870 871 // Perform security check for access to the global object. 872 ASSERT(current_map->IsJSGlobalProxyMap() || 873 !current_map->is_access_check_needed()); 874 if (current_map->IsJSGlobalProxyMap()) { 875 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 876 } 877 878 // Return the register containing the holder. 879 return reg; 880 } 881 882 883 void LoadStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 884 if (!miss->is_unused()) { 885 Label success; 886 __ jmp(&success); 887 __ bind(miss); 888 TailCallBuiltin(masm(), MissBuiltin(kind())); 889 __ bind(&success); 890 } 891 } 892 893 894 void StoreStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 895 if (!miss->is_unused()) { 896 Label success; 897 __ jmp(&success); 898 GenerateRestoreName(masm(), miss, name); 899 TailCallBuiltin(masm(), MissBuiltin(kind())); 900 __ bind(&success); 901 } 902 } 903 904 905 Register LoadStubCompiler::CallbackHandlerFrontend( 906 Handle<HeapType> type, 907 Register object_reg, 908 Handle<JSObject> holder, 909 Handle<Name> name, 910 Handle<Object> callback) { 911 Label miss; 912 913 Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); 914 915 if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { 916 ASSERT(!reg.is(scratch2())); 917 ASSERT(!reg.is(scratch3())); 918 Register dictionary = scratch1(); 919 bool must_preserve_dictionary_reg = reg.is(dictionary); 920 921 // Load the properties dictionary. 922 if (must_preserve_dictionary_reg) { 923 __ push(dictionary); 924 } 925 __ mov(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset)); 926 927 // Probe the dictionary. 928 Label probe_done, pop_and_miss; 929 NameDictionaryLookupStub::GeneratePositiveLookup(masm(), 930 &pop_and_miss, 931 &probe_done, 932 dictionary, 933 this->name(), 934 scratch2(), 935 scratch3()); 936 __ bind(&pop_and_miss); 937 if (must_preserve_dictionary_reg) { 938 __ pop(dictionary); 939 } 940 __ jmp(&miss); 941 __ bind(&probe_done); 942 943 // If probing finds an entry in the dictionary, scratch2 contains the 944 // index into the dictionary. Check that the value is the callback. 945 Register index = scratch2(); 946 const int kElementsStartOffset = 947 NameDictionary::kHeaderSize + 948 NameDictionary::kElementsStartIndex * kPointerSize; 949 const int kValueOffset = kElementsStartOffset + kPointerSize; 950 __ mov(scratch3(), 951 Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag)); 952 if (must_preserve_dictionary_reg) { 953 __ pop(dictionary); 954 } 955 __ cmp(scratch3(), callback); 956 __ j(not_equal, &miss); 957 } 958 959 HandlerFrontendFooter(name, &miss); 960 return reg; 961 } 962 963 964 void LoadStubCompiler::GenerateLoadField(Register reg, 965 Handle<JSObject> holder, 966 FieldIndex field, 967 Representation representation) { 968 if (!reg.is(receiver())) __ mov(receiver(), reg); 969 if (kind() == Code::LOAD_IC) { 970 LoadFieldStub stub(isolate(), field); 971 GenerateTailCall(masm(), stub.GetCode()); 972 } else { 973 KeyedLoadFieldStub stub(isolate(), field); 974 GenerateTailCall(masm(), stub.GetCode()); 975 } 976 } 977 978 979 void LoadStubCompiler::GenerateLoadCallback( 980 Register reg, 981 Handle<ExecutableAccessorInfo> callback) { 982 // Insert additional parameters into the stack frame above return address. 983 ASSERT(!scratch3().is(reg)); 984 __ pop(scratch3()); // Get return address to place it below. 985 986 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); 987 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); 988 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); 989 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); 990 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); 991 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); 992 __ push(receiver()); // receiver 993 // Push data from ExecutableAccessorInfo. 994 if (isolate()->heap()->InNewSpace(callback->data())) { 995 ASSERT(!scratch2().is(reg)); 996 __ mov(scratch2(), Immediate(callback)); 997 __ push(FieldOperand(scratch2(), ExecutableAccessorInfo::kDataOffset)); 998 } else { 999 __ push(Immediate(Handle<Object>(callback->data(), isolate()))); 1000 } 1001 __ push(Immediate(isolate()->factory()->undefined_value())); // ReturnValue 1002 // ReturnValue default value 1003 __ push(Immediate(isolate()->factory()->undefined_value())); 1004 __ push(Immediate(reinterpret_cast<int>(isolate()))); 1005 __ push(reg); // holder 1006 1007 // Save a pointer to where we pushed the arguments. This will be 1008 // passed as the const PropertyAccessorInfo& to the C++ callback. 1009 __ push(esp); 1010 1011 __ push(name()); // name 1012 1013 __ push(scratch3()); // Restore return address. 1014 1015 // Abi for CallApiGetter 1016 Register getter_address = edx; 1017 Address function_address = v8::ToCData<Address>(callback->getter()); 1018 __ mov(getter_address, Immediate(function_address)); 1019 1020 CallApiGetterStub stub(isolate()); 1021 __ TailCallStub(&stub); 1022 } 1023 1024 1025 void LoadStubCompiler::GenerateLoadConstant(Handle<Object> value) { 1026 // Return the constant value. 1027 __ LoadObject(eax, value); 1028 __ ret(0); 1029 } 1030 1031 1032 void LoadStubCompiler::GenerateLoadInterceptor( 1033 Register holder_reg, 1034 Handle<Object> object, 1035 Handle<JSObject> interceptor_holder, 1036 LookupResult* lookup, 1037 Handle<Name> name) { 1038 ASSERT(interceptor_holder->HasNamedInterceptor()); 1039 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); 1040 1041 // So far the most popular follow ups for interceptor loads are FIELD 1042 // and CALLBACKS, so inline only them, other cases may be added 1043 // later. 1044 bool compile_followup_inline = false; 1045 if (lookup->IsFound() && lookup->IsCacheable()) { 1046 if (lookup->IsField()) { 1047 compile_followup_inline = true; 1048 } else if (lookup->type() == CALLBACKS && 1049 lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { 1050 ExecutableAccessorInfo* callback = 1051 ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); 1052 compile_followup_inline = callback->getter() != NULL && 1053 callback->IsCompatibleReceiver(*object); 1054 } 1055 } 1056 1057 if (compile_followup_inline) { 1058 // Compile the interceptor call, followed by inline code to load the 1059 // property from further up the prototype chain if the call fails. 1060 // Check that the maps haven't changed. 1061 ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 1062 1063 // Preserve the receiver register explicitly whenever it is different from 1064 // the holder and it is needed should the interceptor return without any 1065 // result. The CALLBACKS case needs the receiver to be passed into C++ code, 1066 // the FIELD case might cause a miss during the prototype check. 1067 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); 1068 bool must_preserve_receiver_reg = !receiver().is(holder_reg) && 1069 (lookup->type() == CALLBACKS || must_perfrom_prototype_check); 1070 1071 // Save necessary data before invoking an interceptor. 1072 // Requires a frame to make GC aware of pushed pointers. 1073 { 1074 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 1075 1076 if (must_preserve_receiver_reg) { 1077 __ push(receiver()); 1078 } 1079 __ push(holder_reg); 1080 __ push(this->name()); 1081 1082 // Invoke an interceptor. Note: map checks from receiver to 1083 // interceptor's holder has been compiled before (see a caller 1084 // of this method.) 1085 CompileCallLoadPropertyWithInterceptor( 1086 masm(), receiver(), holder_reg, this->name(), interceptor_holder, 1087 IC::kLoadPropertyWithInterceptorOnly); 1088 1089 // Check if interceptor provided a value for property. If it's 1090 // the case, return immediately. 1091 Label interceptor_failed; 1092 __ cmp(eax, factory()->no_interceptor_result_sentinel()); 1093 __ j(equal, &interceptor_failed); 1094 frame_scope.GenerateLeaveFrame(); 1095 __ ret(0); 1096 1097 // Clobber registers when generating debug-code to provoke errors. 1098 __ bind(&interceptor_failed); 1099 if (FLAG_debug_code) { 1100 __ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue))); 1101 __ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue))); 1102 __ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue))); 1103 } 1104 1105 __ pop(this->name()); 1106 __ pop(holder_reg); 1107 if (must_preserve_receiver_reg) { 1108 __ pop(receiver()); 1109 } 1110 1111 // Leave the internal frame. 1112 } 1113 1114 GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); 1115 } else { // !compile_followup_inline 1116 // Call the runtime system to load the interceptor. 1117 // Check that the maps haven't changed. 1118 __ pop(scratch2()); // save old return address 1119 PushInterceptorArguments(masm(), receiver(), holder_reg, 1120 this->name(), interceptor_holder); 1121 __ push(scratch2()); // restore old return address 1122 1123 ExternalReference ref = 1124 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptor), 1125 isolate()); 1126 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); 1127 } 1128 } 1129 1130 1131 Handle<Code> StoreStubCompiler::CompileStoreCallback( 1132 Handle<JSObject> object, 1133 Handle<JSObject> holder, 1134 Handle<Name> name, 1135 Handle<ExecutableAccessorInfo> callback) { 1136 Register holder_reg = HandlerFrontend( 1137 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); 1138 1139 __ pop(scratch1()); // remove the return address 1140 __ push(receiver()); 1141 __ push(holder_reg); 1142 __ Push(callback); 1143 __ Push(name); 1144 __ push(value()); 1145 __ push(scratch1()); // restore return address 1146 1147 // Do tail-call to the runtime system. 1148 ExternalReference store_callback_property = 1149 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); 1150 __ TailCallExternalReference(store_callback_property, 5, 1); 1151 1152 // Return the generated code. 1153 return GetCode(kind(), Code::FAST, name); 1154 } 1155 1156 1157 #undef __ 1158 #define __ ACCESS_MASM(masm) 1159 1160 1161 void StoreStubCompiler::GenerateStoreViaSetter( 1162 MacroAssembler* masm, 1163 Handle<HeapType> type, 1164 Register receiver, 1165 Handle<JSFunction> setter) { 1166 // ----------- S t a t e ------------- 1167 // -- esp[0] : return address 1168 // ----------------------------------- 1169 { 1170 FrameScope scope(masm, StackFrame::INTERNAL); 1171 1172 // Save value register, so we can restore it later. 1173 __ push(value()); 1174 1175 if (!setter.is_null()) { 1176 // Call the JavaScript setter with receiver and value on the stack. 1177 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 1178 // Swap in the global receiver. 1179 __ mov(receiver, 1180 FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); 1181 } 1182 __ push(receiver); 1183 __ push(value()); 1184 ParameterCount actual(1); 1185 ParameterCount expected(setter); 1186 __ InvokeFunction(setter, expected, actual, 1187 CALL_FUNCTION, NullCallWrapper()); 1188 } else { 1189 // If we generate a global code snippet for deoptimization only, remember 1190 // the place to continue after deoptimization. 1191 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 1192 } 1193 1194 // We have to return the passed value, not the return value of the setter. 1195 __ pop(eax); 1196 1197 // Restore context register. 1198 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1199 } 1200 __ ret(0); 1201 } 1202 1203 1204 #undef __ 1205 #define __ ACCESS_MASM(masm()) 1206 1207 1208 Handle<Code> StoreStubCompiler::CompileStoreInterceptor( 1209 Handle<JSObject> object, 1210 Handle<Name> name) { 1211 __ pop(scratch1()); // remove the return address 1212 __ push(receiver()); 1213 __ push(this->name()); 1214 __ push(value()); 1215 __ push(scratch1()); // restore return address 1216 1217 // Do tail-call to the runtime system. 1218 ExternalReference store_ic_property = 1219 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); 1220 __ TailCallExternalReference(store_ic_property, 3, 1); 1221 1222 // Return the generated code. 1223 return GetCode(kind(), Code::FAST, name); 1224 } 1225 1226 1227 void StoreStubCompiler::GenerateStoreArrayLength() { 1228 // Prepare tail call to StoreIC_ArrayLength. 1229 __ pop(scratch1()); // remove the return address 1230 __ push(receiver()); 1231 __ push(value()); 1232 __ push(scratch1()); // restore return address 1233 1234 ExternalReference ref = 1235 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), 1236 masm()->isolate()); 1237 __ TailCallExternalReference(ref, 2, 1); 1238 } 1239 1240 1241 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( 1242 MapHandleList* receiver_maps, 1243 CodeHandleList* handler_stubs, 1244 MapHandleList* transitioned_maps) { 1245 Label miss; 1246 __ JumpIfSmi(receiver(), &miss, Label::kNear); 1247 __ mov(scratch1(), FieldOperand(receiver(), HeapObject::kMapOffset)); 1248 for (int i = 0; i < receiver_maps->length(); ++i) { 1249 __ cmp(scratch1(), receiver_maps->at(i)); 1250 if (transitioned_maps->at(i).is_null()) { 1251 __ j(equal, handler_stubs->at(i)); 1252 } else { 1253 Label next_map; 1254 __ j(not_equal, &next_map, Label::kNear); 1255 __ mov(transition_map(), Immediate(transitioned_maps->at(i))); 1256 __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET); 1257 __ bind(&next_map); 1258 } 1259 } 1260 __ bind(&miss); 1261 TailCallBuiltin(masm(), MissBuiltin(kind())); 1262 1263 // Return the generated code. 1264 return GetICCode( 1265 kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); 1266 } 1267 1268 1269 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<HeapType> type, 1270 Handle<JSObject> last, 1271 Handle<Name> name) { 1272 NonexistentHandlerFrontend(type, last, name); 1273 1274 // Return undefined if maps of the full prototype chain are still the 1275 // same and no global property with this name contains a value. 1276 __ mov(eax, isolate()->factory()->undefined_value()); 1277 __ ret(0); 1278 1279 // Return the generated code. 1280 return GetCode(kind(), Code::FAST, name); 1281 } 1282 1283 1284 Register* LoadStubCompiler::registers() { 1285 // receiver, name, scratch1, scratch2, scratch3, scratch4. 1286 static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg }; 1287 return registers; 1288 } 1289 1290 1291 Register* KeyedLoadStubCompiler::registers() { 1292 // receiver, name, scratch1, scratch2, scratch3, scratch4. 1293 static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg }; 1294 return registers; 1295 } 1296 1297 1298 Register StoreStubCompiler::value() { 1299 return eax; 1300 } 1301 1302 1303 Register* StoreStubCompiler::registers() { 1304 // receiver, name, scratch1, scratch2, scratch3. 1305 static Register registers[] = { edx, ecx, ebx, edi, no_reg }; 1306 return registers; 1307 } 1308 1309 1310 Register* KeyedStoreStubCompiler::registers() { 1311 // receiver, name, scratch1, scratch2, scratch3. 1312 static Register registers[] = { edx, ecx, ebx, edi, no_reg }; 1313 return registers; 1314 } 1315 1316 1317 #undef __ 1318 #define __ ACCESS_MASM(masm) 1319 1320 1321 void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, 1322 Handle<HeapType> type, 1323 Register receiver, 1324 Handle<JSFunction> getter) { 1325 { 1326 FrameScope scope(masm, StackFrame::INTERNAL); 1327 1328 if (!getter.is_null()) { 1329 // Call the JavaScript getter with the receiver on the stack. 1330 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 1331 // Swap in the global receiver. 1332 __ mov(receiver, 1333 FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); 1334 } 1335 __ push(receiver); 1336 ParameterCount actual(0); 1337 ParameterCount expected(getter); 1338 __ InvokeFunction(getter, expected, actual, 1339 CALL_FUNCTION, NullCallWrapper()); 1340 } else { 1341 // If we generate a global code snippet for deoptimization only, remember 1342 // the place to continue after deoptimization. 1343 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 1344 } 1345 1346 // Restore context register. 1347 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1348 } 1349 __ ret(0); 1350 } 1351 1352 1353 #undef __ 1354 #define __ ACCESS_MASM(masm()) 1355 1356 1357 Handle<Code> LoadStubCompiler::CompileLoadGlobal( 1358 Handle<HeapType> type, 1359 Handle<GlobalObject> global, 1360 Handle<PropertyCell> cell, 1361 Handle<Name> name, 1362 bool is_dont_delete) { 1363 Label miss; 1364 1365 HandlerFrontendHeader(type, receiver(), global, name, &miss); 1366 // Get the value from the cell. 1367 if (masm()->serializer_enabled()) { 1368 __ mov(eax, Immediate(cell)); 1369 __ mov(eax, FieldOperand(eax, PropertyCell::kValueOffset)); 1370 } else { 1371 __ mov(eax, Operand::ForCell(cell)); 1372 } 1373 1374 // Check for deleted property if property can actually be deleted. 1375 if (!is_dont_delete) { 1376 __ cmp(eax, factory()->the_hole_value()); 1377 __ j(equal, &miss); 1378 } else if (FLAG_debug_code) { 1379 __ cmp(eax, factory()->the_hole_value()); 1380 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole); 1381 } 1382 1383 Counters* counters = isolate()->counters(); 1384 __ IncrementCounter(counters->named_load_global_stub(), 1); 1385 // The code above already loads the result into the return register. 1386 __ ret(0); 1387 1388 HandlerFrontendFooter(name, &miss); 1389 1390 // Return the generated code. 1391 return GetCode(kind(), Code::NORMAL, name); 1392 } 1393 1394 1395 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( 1396 TypeHandleList* types, 1397 CodeHandleList* handlers, 1398 Handle<Name> name, 1399 Code::StubType type, 1400 IcCheckType check) { 1401 Label miss; 1402 1403 if (check == PROPERTY && 1404 (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC)) { 1405 __ cmp(this->name(), Immediate(name)); 1406 __ j(not_equal, &miss); 1407 } 1408 1409 Label number_case; 1410 Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; 1411 __ JumpIfSmi(receiver(), smi_target); 1412 1413 Register map_reg = scratch1(); 1414 __ mov(map_reg, FieldOperand(receiver(), HeapObject::kMapOffset)); 1415 int receiver_count = types->length(); 1416 int number_of_handled_maps = 0; 1417 for (int current = 0; current < receiver_count; ++current) { 1418 Handle<HeapType> type = types->at(current); 1419 Handle<Map> map = IC::TypeToMap(*type, isolate()); 1420 if (!map->is_deprecated()) { 1421 number_of_handled_maps++; 1422 __ cmp(map_reg, map); 1423 if (type->Is(HeapType::Number())) { 1424 ASSERT(!number_case.is_unused()); 1425 __ bind(&number_case); 1426 } 1427 __ j(equal, handlers->at(current)); 1428 } 1429 } 1430 ASSERT(number_of_handled_maps != 0); 1431 1432 __ bind(&miss); 1433 TailCallBuiltin(masm(), MissBuiltin(kind())); 1434 1435 // Return the generated code. 1436 InlineCacheState state = 1437 number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC; 1438 return GetICCode(kind(), type, name, state); 1439 } 1440 1441 1442 #undef __ 1443 #define __ ACCESS_MASM(masm) 1444 1445 1446 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( 1447 MacroAssembler* masm) { 1448 // ----------- S t a t e ------------- 1449 // -- ecx : key 1450 // -- edx : receiver 1451 // -- esp[0] : return address 1452 // ----------------------------------- 1453 Label slow, miss; 1454 1455 // This stub is meant to be tail-jumped to, the receiver must already 1456 // have been verified by the caller to not be a smi. 1457 __ JumpIfNotSmi(ecx, &miss); 1458 __ mov(ebx, ecx); 1459 __ SmiUntag(ebx); 1460 __ mov(eax, FieldOperand(edx, JSObject::kElementsOffset)); 1461 1462 // Push receiver on the stack to free up a register for the dictionary 1463 // probing. 1464 __ push(edx); 1465 __ LoadFromNumberDictionary(&slow, eax, ecx, ebx, edx, edi, eax); 1466 // Pop receiver before returning. 1467 __ pop(edx); 1468 __ ret(0); 1469 1470 __ bind(&slow); 1471 __ pop(edx); 1472 1473 // ----------- S t a t e ------------- 1474 // -- ecx : key 1475 // -- edx : receiver 1476 // -- esp[0] : return address 1477 // ----------------------------------- 1478 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); 1479 1480 __ bind(&miss); 1481 // ----------- S t a t e ------------- 1482 // -- ecx : key 1483 // -- edx : receiver 1484 // -- esp[0] : return address 1485 // ----------------------------------- 1486 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); 1487 } 1488 1489 1490 #undef __ 1491 1492 } } // namespace v8::internal 1493 1494 #endif // V8_TARGET_ARCH_IA32 1495