Home | History | Annotate | Download | only in ia32
      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