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_X87 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 __ push(value_reg); 531 __ fild_s(Operand(esp, 0)); 532 __ pop(value_reg); 533 __ SmiTag(value_reg); 534 __ jmp(&do_store); 535 536 __ bind(&heap_number); 537 __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(), 538 miss_label, DONT_DO_SMI_CHECK); 539 __ fld_d(FieldOperand(value_reg, HeapNumber::kValueOffset)); 540 541 __ bind(&do_store); 542 __ fstp_d(FieldOperand(storage_reg, HeapNumber::kValueOffset)); 543 } 544 545 // Stub never generated for non-global objects that require access 546 // checks. 547 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 548 549 // Perform map transition for the receiver if necessary. 550 if (details.type() == FIELD && 551 object->map()->unused_property_fields() == 0) { 552 // The properties must be extended before we can store the value. 553 // We jump to a runtime call that extends the properties array. 554 __ pop(scratch1); // Return address. 555 __ push(receiver_reg); 556 __ push(Immediate(transition)); 557 __ push(value_reg); 558 __ push(scratch1); 559 __ TailCallExternalReference( 560 ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage), 561 masm->isolate()), 562 3, 563 1); 564 return; 565 } 566 567 // Update the map of the object. 568 __ mov(scratch1, Immediate(transition)); 569 __ mov(FieldOperand(receiver_reg, HeapObject::kMapOffset), scratch1); 570 571 // Update the write barrier for the map field. 572 __ RecordWriteField(receiver_reg, 573 HeapObject::kMapOffset, 574 scratch1, 575 scratch2, 576 OMIT_REMEMBERED_SET, 577 OMIT_SMI_CHECK); 578 579 if (details.type() == CONSTANT) { 580 ASSERT(value_reg.is(eax)); 581 __ ret(0); 582 return; 583 } 584 585 int index = transition->instance_descriptors()->GetFieldIndex( 586 transition->LastAdded()); 587 588 // Adjust for the number of properties stored in the object. Even in the 589 // face of a transition we can use the old map here because the size of the 590 // object and the number of in-object properties is not going to change. 591 index -= object->map()->inobject_properties(); 592 593 SmiCheck smi_check = representation.IsTagged() 594 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 595 // TODO(verwaest): Share this code as a code stub. 596 if (index < 0) { 597 // Set the property straight into the object. 598 int offset = object->map()->instance_size() + (index * kPointerSize); 599 if (representation.IsDouble()) { 600 __ mov(FieldOperand(receiver_reg, offset), storage_reg); 601 } else { 602 __ mov(FieldOperand(receiver_reg, offset), value_reg); 603 } 604 605 if (!representation.IsSmi()) { 606 // Update the write barrier for the array address. 607 if (!representation.IsDouble()) { 608 __ mov(storage_reg, value_reg); 609 } 610 __ RecordWriteField(receiver_reg, 611 offset, 612 storage_reg, 613 scratch1, 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 EMIT_REMEMBERED_SET, 638 smi_check); 639 } 640 } 641 642 // Return the value (register eax). 643 ASSERT(value_reg.is(eax)); 644 __ ret(0); 645 } 646 647 648 // Both name_reg and receiver_reg are preserved on jumps to miss_label, 649 // but may be destroyed if store is successful. 650 void StoreStubCompiler::GenerateStoreField(MacroAssembler* masm, 651 Handle<JSObject> object, 652 LookupResult* lookup, 653 Register receiver_reg, 654 Register name_reg, 655 Register value_reg, 656 Register scratch1, 657 Register scratch2, 658 Label* miss_label) { 659 // Stub never generated for non-global objects that require access 660 // checks. 661 ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); 662 663 FieldIndex index = lookup->GetFieldIndex(); 664 665 Representation representation = lookup->representation(); 666 ASSERT(!representation.IsNone()); 667 if (representation.IsSmi()) { 668 __ JumpIfNotSmi(value_reg, miss_label); 669 } else if (representation.IsHeapObject()) { 670 __ JumpIfSmi(value_reg, miss_label); 671 HeapType* field_type = lookup->GetFieldType(); 672 HeapType::Iterator<Map> it = field_type->Classes(); 673 if (!it.Done()) { 674 Label do_store; 675 while (true) { 676 __ CompareMap(value_reg, it.Current()); 677 it.Advance(); 678 if (it.Done()) { 679 __ j(not_equal, miss_label); 680 break; 681 } 682 __ j(equal, &do_store, Label::kNear); 683 } 684 __ bind(&do_store); 685 } 686 } else if (representation.IsDouble()) { 687 // Load the double storage. 688 if (index.is_inobject()) { 689 __ mov(scratch1, FieldOperand(receiver_reg, index.offset())); 690 } else { 691 __ mov(scratch1, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); 692 __ mov(scratch1, FieldOperand(scratch1, index.offset())); 693 } 694 695 // Store the value into the storage. 696 Label do_store, heap_number; 697 __ JumpIfNotSmi(value_reg, &heap_number); 698 __ SmiUntag(value_reg); 699 __ push(value_reg); 700 __ fild_s(Operand(esp, 0)); 701 __ pop(value_reg); 702 __ SmiTag(value_reg); 703 __ jmp(&do_store); 704 __ bind(&heap_number); 705 __ CheckMap(value_reg, masm->isolate()->factory()->heap_number_map(), 706 miss_label, DONT_DO_SMI_CHECK); 707 __ fld_d(FieldOperand(value_reg, HeapNumber::kValueOffset)); 708 __ bind(&do_store); 709 __ fstp_d(FieldOperand(scratch1, HeapNumber::kValueOffset)); 710 // Return the value (register eax). 711 ASSERT(value_reg.is(eax)); 712 __ ret(0); 713 return; 714 } 715 716 ASSERT(!representation.IsDouble()); 717 // TODO(verwaest): Share this code as a code stub. 718 SmiCheck smi_check = representation.IsTagged() 719 ? INLINE_SMI_CHECK : OMIT_SMI_CHECK; 720 if (index.is_inobject()) { 721 // Set the property straight into the object. 722 __ mov(FieldOperand(receiver_reg, index.offset()), value_reg); 723 724 if (!representation.IsSmi()) { 725 // Update the write barrier for the array address. 726 // Pass the value being stored in the now unused name_reg. 727 __ mov(name_reg, value_reg); 728 __ RecordWriteField(receiver_reg, 729 index.offset(), 730 name_reg, 731 scratch1, 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 EMIT_REMEMBERED_SET, 750 smi_check); 751 } 752 } 753 754 // Return the value (register eax). 755 ASSERT(value_reg.is(eax)); 756 __ ret(0); 757 } 758 759 760 void StubCompiler::GenerateTailCall(MacroAssembler* masm, Handle<Code> code) { 761 __ jmp(code, RelocInfo::CODE_TARGET); 762 } 763 764 765 #undef __ 766 #define __ ACCESS_MASM(masm()) 767 768 769 Register StubCompiler::CheckPrototypes(Handle<HeapType> type, 770 Register object_reg, 771 Handle<JSObject> holder, 772 Register holder_reg, 773 Register scratch1, 774 Register scratch2, 775 Handle<Name> name, 776 Label* miss, 777 PrototypeCheckType check) { 778 Handle<Map> receiver_map(IC::TypeToMap(*type, isolate())); 779 780 // Make sure there's no overlap between holder and object registers. 781 ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 782 ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) 783 && !scratch2.is(scratch1)); 784 785 // Keep track of the current object in register reg. 786 Register reg = object_reg; 787 int depth = 0; 788 789 Handle<JSObject> current = Handle<JSObject>::null(); 790 if (type->IsConstant()) current = 791 Handle<JSObject>::cast(type->AsConstant()->Value()); 792 Handle<JSObject> prototype = Handle<JSObject>::null(); 793 Handle<Map> current_map = receiver_map; 794 Handle<Map> holder_map(holder->map()); 795 // Traverse the prototype chain and check the maps in the prototype chain for 796 // fast and global objects or do negative lookup for normal objects. 797 while (!current_map.is_identical_to(holder_map)) { 798 ++depth; 799 800 // Only global objects and objects that do not require access 801 // checks are allowed in stubs. 802 ASSERT(current_map->IsJSGlobalProxyMap() || 803 !current_map->is_access_check_needed()); 804 805 prototype = handle(JSObject::cast(current_map->prototype())); 806 if (current_map->is_dictionary_map() && 807 !current_map->IsJSGlobalObjectMap() && 808 !current_map->IsJSGlobalProxyMap()) { 809 if (!name->IsUniqueName()) { 810 ASSERT(name->IsString()); 811 name = factory()->InternalizeString(Handle<String>::cast(name)); 812 } 813 ASSERT(current.is_null() || 814 current->property_dictionary()->FindEntry(name) == 815 NameDictionary::kNotFound); 816 817 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, 818 scratch1, scratch2); 819 820 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 821 reg = holder_reg; // From now on the object will be in holder_reg. 822 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 823 } else { 824 bool in_new_space = heap()->InNewSpace(*prototype); 825 if (depth != 1 || check == CHECK_ALL_MAPS) { 826 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 827 } 828 829 // Check access rights to the global object. This has to happen after 830 // the map check so that we know that the object is actually a global 831 // object. 832 if (current_map->IsJSGlobalProxyMap()) { 833 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 834 } else if (current_map->IsJSGlobalObjectMap()) { 835 GenerateCheckPropertyCell( 836 masm(), Handle<JSGlobalObject>::cast(current), name, 837 scratch2, miss); 838 } 839 840 if (in_new_space) { 841 // Save the map in scratch1 for later. 842 __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 843 } 844 845 reg = holder_reg; // From now on the object will be in holder_reg. 846 847 if (in_new_space) { 848 // The prototype is in new space; we cannot store a reference to it 849 // in the code. Load it from the map. 850 __ mov(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 851 } else { 852 // The prototype is in old space; load it directly. 853 __ mov(reg, prototype); 854 } 855 } 856 857 // Go to the next object in the prototype chain. 858 current = prototype; 859 current_map = handle(current->map()); 860 } 861 862 // Log the check depth. 863 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 864 865 if (depth != 0 || check == CHECK_ALL_MAPS) { 866 // Check the holder map. 867 __ CheckMap(reg, current_map, miss, DONT_DO_SMI_CHECK); 868 } 869 870 // Perform security check for access to the global object. 871 ASSERT(current_map->IsJSGlobalProxyMap() || 872 !current_map->is_access_check_needed()); 873 if (current_map->IsJSGlobalProxyMap()) { 874 __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss); 875 } 876 877 // Return the register containing the holder. 878 return reg; 879 } 880 881 882 void LoadStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 883 if (!miss->is_unused()) { 884 Label success; 885 __ jmp(&success); 886 __ bind(miss); 887 TailCallBuiltin(masm(), MissBuiltin(kind())); 888 __ bind(&success); 889 } 890 } 891 892 893 void StoreStubCompiler::HandlerFrontendFooter(Handle<Name> name, Label* miss) { 894 if (!miss->is_unused()) { 895 Label success; 896 __ jmp(&success); 897 GenerateRestoreName(masm(), miss, name); 898 TailCallBuiltin(masm(), MissBuiltin(kind())); 899 __ bind(&success); 900 } 901 } 902 903 904 Register LoadStubCompiler::CallbackHandlerFrontend( 905 Handle<HeapType> type, 906 Register object_reg, 907 Handle<JSObject> holder, 908 Handle<Name> name, 909 Handle<Object> callback) { 910 Label miss; 911 912 Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); 913 914 if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) { 915 ASSERT(!reg.is(scratch2())); 916 ASSERT(!reg.is(scratch3())); 917 Register dictionary = scratch1(); 918 bool must_preserve_dictionary_reg = reg.is(dictionary); 919 920 // Load the properties dictionary. 921 if (must_preserve_dictionary_reg) { 922 __ push(dictionary); 923 } 924 __ mov(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset)); 925 926 // Probe the dictionary. 927 Label probe_done, pop_and_miss; 928 NameDictionaryLookupStub::GeneratePositiveLookup(masm(), 929 &pop_and_miss, 930 &probe_done, 931 dictionary, 932 this->name(), 933 scratch2(), 934 scratch3()); 935 __ bind(&pop_and_miss); 936 if (must_preserve_dictionary_reg) { 937 __ pop(dictionary); 938 } 939 __ jmp(&miss); 940 __ bind(&probe_done); 941 942 // If probing finds an entry in the dictionary, scratch2 contains the 943 // index into the dictionary. Check that the value is the callback. 944 Register index = scratch2(); 945 const int kElementsStartOffset = 946 NameDictionary::kHeaderSize + 947 NameDictionary::kElementsStartIndex * kPointerSize; 948 const int kValueOffset = kElementsStartOffset + kPointerSize; 949 __ mov(scratch3(), 950 Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag)); 951 if (must_preserve_dictionary_reg) { 952 __ pop(dictionary); 953 } 954 __ cmp(scratch3(), callback); 955 __ j(not_equal, &miss); 956 } 957 958 HandlerFrontendFooter(name, &miss); 959 return reg; 960 } 961 962 963 void LoadStubCompiler::GenerateLoadField(Register reg, 964 Handle<JSObject> holder, 965 FieldIndex field, 966 Representation representation) { 967 if (!reg.is(receiver())) __ mov(receiver(), reg); 968 if (kind() == Code::LOAD_IC) { 969 LoadFieldStub stub(isolate(), field); 970 GenerateTailCall(masm(), stub.GetCode()); 971 } else { 972 KeyedLoadFieldStub stub(isolate(), field); 973 GenerateTailCall(masm(), stub.GetCode()); 974 } 975 } 976 977 978 void LoadStubCompiler::GenerateLoadCallback( 979 Register reg, 980 Handle<ExecutableAccessorInfo> callback) { 981 // Insert additional parameters into the stack frame above return address. 982 ASSERT(!scratch3().is(reg)); 983 __ pop(scratch3()); // Get return address to place it below. 984 985 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); 986 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); 987 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); 988 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); 989 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); 990 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); 991 __ push(receiver()); // receiver 992 // Push data from ExecutableAccessorInfo. 993 if (isolate()->heap()->InNewSpace(callback->data())) { 994 ASSERT(!scratch2().is(reg)); 995 __ mov(scratch2(), Immediate(callback)); 996 __ push(FieldOperand(scratch2(), ExecutableAccessorInfo::kDataOffset)); 997 } else { 998 __ push(Immediate(Handle<Object>(callback->data(), isolate()))); 999 } 1000 __ push(Immediate(isolate()->factory()->undefined_value())); // ReturnValue 1001 // ReturnValue default value 1002 __ push(Immediate(isolate()->factory()->undefined_value())); 1003 __ push(Immediate(reinterpret_cast<int>(isolate()))); 1004 __ push(reg); // holder 1005 1006 // Save a pointer to where we pushed the arguments. This will be 1007 // passed as the const PropertyAccessorInfo& to the C++ callback. 1008 __ push(esp); 1009 1010 __ push(name()); // name 1011 1012 __ push(scratch3()); // Restore return address. 1013 1014 // Abi for CallApiGetter 1015 Register getter_address = edx; 1016 Address function_address = v8::ToCData<Address>(callback->getter()); 1017 __ mov(getter_address, Immediate(function_address)); 1018 1019 CallApiGetterStub stub(isolate()); 1020 __ TailCallStub(&stub); 1021 } 1022 1023 1024 void LoadStubCompiler::GenerateLoadConstant(Handle<Object> value) { 1025 // Return the constant value. 1026 __ LoadObject(eax, value); 1027 __ ret(0); 1028 } 1029 1030 1031 void LoadStubCompiler::GenerateLoadInterceptor( 1032 Register holder_reg, 1033 Handle<Object> object, 1034 Handle<JSObject> interceptor_holder, 1035 LookupResult* lookup, 1036 Handle<Name> name) { 1037 ASSERT(interceptor_holder->HasNamedInterceptor()); 1038 ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); 1039 1040 // So far the most popular follow ups for interceptor loads are FIELD 1041 // and CALLBACKS, so inline only them, other cases may be added 1042 // later. 1043 bool compile_followup_inline = false; 1044 if (lookup->IsFound() && lookup->IsCacheable()) { 1045 if (lookup->IsField()) { 1046 compile_followup_inline = true; 1047 } else if (lookup->type() == CALLBACKS && 1048 lookup->GetCallbackObject()->IsExecutableAccessorInfo()) { 1049 ExecutableAccessorInfo* callback = 1050 ExecutableAccessorInfo::cast(lookup->GetCallbackObject()); 1051 compile_followup_inline = callback->getter() != NULL && 1052 callback->IsCompatibleReceiver(*object); 1053 } 1054 } 1055 1056 if (compile_followup_inline) { 1057 // Compile the interceptor call, followed by inline code to load the 1058 // property from further up the prototype chain if the call fails. 1059 // Check that the maps haven't changed. 1060 ASSERT(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 1061 1062 // Preserve the receiver register explicitly whenever it is different from 1063 // the holder and it is needed should the interceptor return without any 1064 // result. The CALLBACKS case needs the receiver to be passed into C++ code, 1065 // the FIELD case might cause a miss during the prototype check. 1066 bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder(); 1067 bool must_preserve_receiver_reg = !receiver().is(holder_reg) && 1068 (lookup->type() == CALLBACKS || must_perfrom_prototype_check); 1069 1070 // Save necessary data before invoking an interceptor. 1071 // Requires a frame to make GC aware of pushed pointers. 1072 { 1073 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 1074 1075 if (must_preserve_receiver_reg) { 1076 __ push(receiver()); 1077 } 1078 __ push(holder_reg); 1079 __ push(this->name()); 1080 1081 // Invoke an interceptor. Note: map checks from receiver to 1082 // interceptor's holder has been compiled before (see a caller 1083 // of this method.) 1084 CompileCallLoadPropertyWithInterceptor( 1085 masm(), receiver(), holder_reg, this->name(), interceptor_holder, 1086 IC::kLoadPropertyWithInterceptorOnly); 1087 1088 // Check if interceptor provided a value for property. If it's 1089 // the case, return immediately. 1090 Label interceptor_failed; 1091 __ cmp(eax, factory()->no_interceptor_result_sentinel()); 1092 __ j(equal, &interceptor_failed); 1093 frame_scope.GenerateLeaveFrame(); 1094 __ ret(0); 1095 1096 // Clobber registers when generating debug-code to provoke errors. 1097 __ bind(&interceptor_failed); 1098 if (FLAG_debug_code) { 1099 __ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue))); 1100 __ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue))); 1101 __ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue))); 1102 } 1103 1104 __ pop(this->name()); 1105 __ pop(holder_reg); 1106 if (must_preserve_receiver_reg) { 1107 __ pop(receiver()); 1108 } 1109 1110 // Leave the internal frame. 1111 } 1112 1113 GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup); 1114 } else { // !compile_followup_inline 1115 // Call the runtime system to load the interceptor. 1116 // Check that the maps haven't changed. 1117 __ pop(scratch2()); // save old return address 1118 PushInterceptorArguments(masm(), receiver(), holder_reg, 1119 this->name(), interceptor_holder); 1120 __ push(scratch2()); // restore old return address 1121 1122 ExternalReference ref = 1123 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptor), 1124 isolate()); 1125 __ TailCallExternalReference(ref, StubCache::kInterceptorArgsLength, 1); 1126 } 1127 } 1128 1129 1130 Handle<Code> StoreStubCompiler::CompileStoreCallback( 1131 Handle<JSObject> object, 1132 Handle<JSObject> holder, 1133 Handle<Name> name, 1134 Handle<ExecutableAccessorInfo> callback) { 1135 Register holder_reg = HandlerFrontend( 1136 IC::CurrentTypeOf(object, isolate()), receiver(), holder, name); 1137 1138 __ pop(scratch1()); // remove the return address 1139 __ push(receiver()); 1140 __ push(holder_reg); 1141 __ Push(callback); 1142 __ Push(name); 1143 __ push(value()); 1144 __ push(scratch1()); // restore return address 1145 1146 // Do tail-call to the runtime system. 1147 ExternalReference store_callback_property = 1148 ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate()); 1149 __ TailCallExternalReference(store_callback_property, 5, 1); 1150 1151 // Return the generated code. 1152 return GetCode(kind(), Code::FAST, name); 1153 } 1154 1155 1156 #undef __ 1157 #define __ ACCESS_MASM(masm) 1158 1159 1160 void StoreStubCompiler::GenerateStoreViaSetter( 1161 MacroAssembler* masm, 1162 Handle<HeapType> type, 1163 Register receiver, 1164 Handle<JSFunction> setter) { 1165 // ----------- S t a t e ------------- 1166 // -- esp[0] : return address 1167 // ----------------------------------- 1168 { 1169 FrameScope scope(masm, StackFrame::INTERNAL); 1170 1171 // Save value register, so we can restore it later. 1172 __ push(value()); 1173 1174 if (!setter.is_null()) { 1175 // Call the JavaScript setter with receiver and value on the stack. 1176 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 1177 // Swap in the global receiver. 1178 __ mov(receiver, 1179 FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); 1180 } 1181 __ push(receiver); 1182 __ push(value()); 1183 ParameterCount actual(1); 1184 ParameterCount expected(setter); 1185 __ InvokeFunction(setter, expected, actual, 1186 CALL_FUNCTION, NullCallWrapper()); 1187 } else { 1188 // If we generate a global code snippet for deoptimization only, remember 1189 // the place to continue after deoptimization. 1190 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 1191 } 1192 1193 // We have to return the passed value, not the return value of the setter. 1194 __ pop(eax); 1195 1196 // Restore context register. 1197 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1198 } 1199 __ ret(0); 1200 } 1201 1202 1203 #undef __ 1204 #define __ ACCESS_MASM(masm()) 1205 1206 1207 Handle<Code> StoreStubCompiler::CompileStoreInterceptor( 1208 Handle<JSObject> object, 1209 Handle<Name> name) { 1210 __ pop(scratch1()); // remove the return address 1211 __ push(receiver()); 1212 __ push(this->name()); 1213 __ push(value()); 1214 __ push(scratch1()); // restore return address 1215 1216 // Do tail-call to the runtime system. 1217 ExternalReference store_ic_property = 1218 ExternalReference(IC_Utility(IC::kStoreInterceptorProperty), isolate()); 1219 __ TailCallExternalReference(store_ic_property, 3, 1); 1220 1221 // Return the generated code. 1222 return GetCode(kind(), Code::FAST, name); 1223 } 1224 1225 1226 void StoreStubCompiler::GenerateStoreArrayLength() { 1227 // Prepare tail call to StoreIC_ArrayLength. 1228 __ pop(scratch1()); // remove the return address 1229 __ push(receiver()); 1230 __ push(value()); 1231 __ push(scratch1()); // restore return address 1232 1233 ExternalReference ref = 1234 ExternalReference(IC_Utility(IC::kStoreIC_ArrayLength), 1235 masm()->isolate()); 1236 __ TailCallExternalReference(ref, 2, 1); 1237 } 1238 1239 1240 Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic( 1241 MapHandleList* receiver_maps, 1242 CodeHandleList* handler_stubs, 1243 MapHandleList* transitioned_maps) { 1244 Label miss; 1245 __ JumpIfSmi(receiver(), &miss, Label::kNear); 1246 __ mov(scratch1(), FieldOperand(receiver(), HeapObject::kMapOffset)); 1247 for (int i = 0; i < receiver_maps->length(); ++i) { 1248 __ cmp(scratch1(), receiver_maps->at(i)); 1249 if (transitioned_maps->at(i).is_null()) { 1250 __ j(equal, handler_stubs->at(i)); 1251 } else { 1252 Label next_map; 1253 __ j(not_equal, &next_map, Label::kNear); 1254 __ mov(transition_map(), Immediate(transitioned_maps->at(i))); 1255 __ jmp(handler_stubs->at(i), RelocInfo::CODE_TARGET); 1256 __ bind(&next_map); 1257 } 1258 } 1259 __ bind(&miss); 1260 TailCallBuiltin(masm(), MissBuiltin(kind())); 1261 1262 // Return the generated code. 1263 return GetICCode( 1264 kind(), Code::NORMAL, factory()->empty_string(), POLYMORPHIC); 1265 } 1266 1267 1268 Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<HeapType> type, 1269 Handle<JSObject> last, 1270 Handle<Name> name) { 1271 NonexistentHandlerFrontend(type, last, name); 1272 1273 // Return undefined if maps of the full prototype chain are still the 1274 // same and no global property with this name contains a value. 1275 __ mov(eax, isolate()->factory()->undefined_value()); 1276 __ ret(0); 1277 1278 // Return the generated code. 1279 return GetCode(kind(), Code::FAST, name); 1280 } 1281 1282 1283 Register* LoadStubCompiler::registers() { 1284 // receiver, name, scratch1, scratch2, scratch3, scratch4. 1285 static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg }; 1286 return registers; 1287 } 1288 1289 1290 Register* KeyedLoadStubCompiler::registers() { 1291 // receiver, name, scratch1, scratch2, scratch3, scratch4. 1292 static Register registers[] = { edx, ecx, ebx, eax, edi, no_reg }; 1293 return registers; 1294 } 1295 1296 1297 Register StoreStubCompiler::value() { 1298 return eax; 1299 } 1300 1301 1302 Register* StoreStubCompiler::registers() { 1303 // receiver, name, scratch1, scratch2, scratch3. 1304 static Register registers[] = { edx, ecx, ebx, edi, no_reg }; 1305 return registers; 1306 } 1307 1308 1309 Register* KeyedStoreStubCompiler::registers() { 1310 // receiver, name, scratch1, scratch2, scratch3. 1311 static Register registers[] = { edx, ecx, ebx, edi, no_reg }; 1312 return registers; 1313 } 1314 1315 1316 #undef __ 1317 #define __ ACCESS_MASM(masm) 1318 1319 1320 void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm, 1321 Handle<HeapType> type, 1322 Register receiver, 1323 Handle<JSFunction> getter) { 1324 { 1325 FrameScope scope(masm, StackFrame::INTERNAL); 1326 1327 if (!getter.is_null()) { 1328 // Call the JavaScript getter with the receiver on the stack. 1329 if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) { 1330 // Swap in the global receiver. 1331 __ mov(receiver, 1332 FieldOperand(receiver, JSGlobalObject::kGlobalReceiverOffset)); 1333 } 1334 __ push(receiver); 1335 ParameterCount actual(0); 1336 ParameterCount expected(getter); 1337 __ InvokeFunction(getter, expected, actual, 1338 CALL_FUNCTION, NullCallWrapper()); 1339 } else { 1340 // If we generate a global code snippet for deoptimization only, remember 1341 // the place to continue after deoptimization. 1342 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 1343 } 1344 1345 // Restore context register. 1346 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 1347 } 1348 __ ret(0); 1349 } 1350 1351 1352 #undef __ 1353 #define __ ACCESS_MASM(masm()) 1354 1355 1356 Handle<Code> LoadStubCompiler::CompileLoadGlobal( 1357 Handle<HeapType> type, 1358 Handle<GlobalObject> global, 1359 Handle<PropertyCell> cell, 1360 Handle<Name> name, 1361 bool is_dont_delete) { 1362 Label miss; 1363 1364 HandlerFrontendHeader(type, receiver(), global, name, &miss); 1365 // Get the value from the cell. 1366 if (masm()->serializer_enabled()) { 1367 __ mov(eax, Immediate(cell)); 1368 __ mov(eax, FieldOperand(eax, PropertyCell::kValueOffset)); 1369 } else { 1370 __ mov(eax, Operand::ForCell(cell)); 1371 } 1372 1373 // Check for deleted property if property can actually be deleted. 1374 if (!is_dont_delete) { 1375 __ cmp(eax, factory()->the_hole_value()); 1376 __ j(equal, &miss); 1377 } else if (FLAG_debug_code) { 1378 __ cmp(eax, factory()->the_hole_value()); 1379 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole); 1380 } 1381 1382 Counters* counters = isolate()->counters(); 1383 __ IncrementCounter(counters->named_load_global_stub(), 1); 1384 // The code above already loads the result into the return register. 1385 __ ret(0); 1386 1387 HandlerFrontendFooter(name, &miss); 1388 1389 // Return the generated code. 1390 return GetCode(kind(), Code::NORMAL, name); 1391 } 1392 1393 1394 Handle<Code> BaseLoadStoreStubCompiler::CompilePolymorphicIC( 1395 TypeHandleList* types, 1396 CodeHandleList* handlers, 1397 Handle<Name> name, 1398 Code::StubType type, 1399 IcCheckType check) { 1400 Label miss; 1401 1402 if (check == PROPERTY && 1403 (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC)) { 1404 __ cmp(this->name(), Immediate(name)); 1405 __ j(not_equal, &miss); 1406 } 1407 1408 Label number_case; 1409 Label* smi_target = IncludesNumberType(types) ? &number_case : &miss; 1410 __ JumpIfSmi(receiver(), smi_target); 1411 1412 Register map_reg = scratch1(); 1413 __ mov(map_reg, FieldOperand(receiver(), HeapObject::kMapOffset)); 1414 int receiver_count = types->length(); 1415 int number_of_handled_maps = 0; 1416 for (int current = 0; current < receiver_count; ++current) { 1417 Handle<HeapType> type = types->at(current); 1418 Handle<Map> map = IC::TypeToMap(*type, isolate()); 1419 if (!map->is_deprecated()) { 1420 number_of_handled_maps++; 1421 __ cmp(map_reg, map); 1422 if (type->Is(HeapType::Number())) { 1423 ASSERT(!number_case.is_unused()); 1424 __ bind(&number_case); 1425 } 1426 __ j(equal, handlers->at(current)); 1427 } 1428 } 1429 ASSERT(number_of_handled_maps != 0); 1430 1431 __ bind(&miss); 1432 TailCallBuiltin(masm(), MissBuiltin(kind())); 1433 1434 // Return the generated code. 1435 InlineCacheState state = 1436 number_of_handled_maps > 1 ? POLYMORPHIC : MONOMORPHIC; 1437 return GetICCode(kind(), type, name, state); 1438 } 1439 1440 1441 #undef __ 1442 #define __ ACCESS_MASM(masm) 1443 1444 1445 void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( 1446 MacroAssembler* masm) { 1447 // ----------- S t a t e ------------- 1448 // -- ecx : key 1449 // -- edx : receiver 1450 // -- esp[0] : return address 1451 // ----------------------------------- 1452 Label slow, miss; 1453 1454 // This stub is meant to be tail-jumped to, the receiver must already 1455 // have been verified by the caller to not be a smi. 1456 __ JumpIfNotSmi(ecx, &miss); 1457 __ mov(ebx, ecx); 1458 __ SmiUntag(ebx); 1459 __ mov(eax, FieldOperand(edx, JSObject::kElementsOffset)); 1460 1461 // Push receiver on the stack to free up a register for the dictionary 1462 // probing. 1463 __ push(edx); 1464 __ LoadFromNumberDictionary(&slow, eax, ecx, ebx, edx, edi, eax); 1465 // Pop receiver before returning. 1466 __ pop(edx); 1467 __ ret(0); 1468 1469 __ bind(&slow); 1470 __ pop(edx); 1471 1472 // ----------- S t a t e ------------- 1473 // -- ecx : key 1474 // -- edx : receiver 1475 // -- esp[0] : return address 1476 // ----------------------------------- 1477 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Slow); 1478 1479 __ bind(&miss); 1480 // ----------- S t a t e ------------- 1481 // -- ecx : key 1482 // -- edx : receiver 1483 // -- esp[0] : return address 1484 // ----------------------------------- 1485 TailCallBuiltin(masm, Builtins::kKeyedLoadIC_Miss); 1486 } 1487 1488 1489 #undef __ 1490 1491 } } // namespace v8::internal 1492 1493 #endif // V8_TARGET_ARCH_X87 1494