Home | History | Annotate | Download | only in x64
      1 // Copyright 2014 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 #if V8_TARGET_ARCH_X64
      6 
      7 #include "src/ic/handler-compiler.h"
      8 
      9 #include "src/api-arguments.h"
     10 #include "src/field-type.h"
     11 #include "src/ic/call-optimization.h"
     12 #include "src/ic/ic.h"
     13 #include "src/isolate-inl.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 #define __ ACCESS_MASM(masm)
     19 
     20 void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
     21                                                 Register slot) {
     22   MacroAssembler* masm = this->masm();
     23   STATIC_ASSERT(LoadWithVectorDescriptor::kSlot <
     24                 LoadWithVectorDescriptor::kVector);
     25   STATIC_ASSERT(StoreWithVectorDescriptor::kSlot <
     26                 StoreWithVectorDescriptor::kVector);
     27   STATIC_ASSERT(StoreTransitionDescriptor::kSlot <
     28                 StoreTransitionDescriptor::kVector);
     29   __ Push(slot);
     30   __ Push(vector);
     31 }
     32 
     33 
     34 void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
     35   MacroAssembler* masm = this->masm();
     36   __ Pop(vector);
     37   __ Pop(slot);
     38 }
     39 
     40 
     41 void PropertyHandlerCompiler::DiscardVectorAndSlot() {
     42   MacroAssembler* masm = this->masm();
     43   // Remove vector and slot.
     44   __ addp(rsp, Immediate(2 * kPointerSize));
     45 }
     46 
     47 void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
     48     MacroAssembler* masm, Label* miss_label, Register receiver,
     49     Handle<Name> name, Register scratch0, Register scratch1) {
     50   DCHECK(name->IsUniqueName());
     51   DCHECK(!receiver.is(scratch0));
     52   Counters* counters = masm->isolate()->counters();
     53   __ IncrementCounter(counters->negative_lookups(), 1);
     54   __ IncrementCounter(counters->negative_lookups_miss(), 1);
     55 
     56   __ movp(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
     57 
     58   const int kInterceptorOrAccessCheckNeededMask =
     59       (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
     60 
     61   // Bail out if the receiver has a named interceptor or requires access checks.
     62   __ testb(FieldOperand(scratch0, Map::kBitFieldOffset),
     63            Immediate(kInterceptorOrAccessCheckNeededMask));
     64   __ j(not_zero, miss_label);
     65 
     66   // Check that receiver is a JSObject.
     67   __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE);
     68   __ j(below, miss_label);
     69 
     70   // Load properties array.
     71   Register properties = scratch0;
     72   __ movp(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
     73 
     74   // Check that the properties array is a dictionary.
     75   __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset),
     76                  Heap::kHashTableMapRootIndex);
     77   __ j(not_equal, miss_label);
     78 
     79   Label done;
     80   NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done,
     81                                                    properties, name, scratch1);
     82   __ bind(&done);
     83   __ DecrementCounter(counters->negative_lookups_miss(), 1);
     84 }
     85 
     86 static void CompileCallLoadPropertyWithInterceptor(
     87     MacroAssembler* masm, Register receiver, Register holder, Register name,
     88     Handle<JSObject> holder_obj, Runtime::FunctionId id) {
     89   DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
     90          Runtime::FunctionForId(id)->nargs);
     91 
     92   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
     93   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
     94   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
     95   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
     96   __ Push(name);
     97   __ Push(receiver);
     98   __ Push(holder);
     99 
    100   __ CallRuntime(id);
    101 }
    102 
    103 
    104 // Generate call to api function.
    105 void PropertyHandlerCompiler::GenerateApiAccessorCall(
    106     MacroAssembler* masm, const CallOptimization& optimization,
    107     Handle<Map> receiver_map, Register receiver, Register scratch,
    108     bool is_store, Register store_parameter, Register accessor_holder,
    109     int accessor_index) {
    110   DCHECK(!accessor_holder.is(scratch));
    111   DCHECK(optimization.is_simple_api_call());
    112 
    113   __ PopReturnAddressTo(scratch);
    114   // receiver
    115   __ Push(receiver);
    116   // Write the arguments to stack frame.
    117   if (is_store) {
    118     DCHECK(!receiver.is(store_parameter));
    119     DCHECK(!scratch.is(store_parameter));
    120     __ Push(store_parameter);
    121   }
    122   __ PushReturnAddressFrom(scratch);
    123   // Stack now matches JSFunction abi.
    124 
    125   // Abi for CallApiCallbackStub.
    126   Register callee = rdi;
    127   Register data = rbx;
    128   Register holder = rcx;
    129   Register api_function_address = rdx;
    130   scratch = no_reg;
    131 
    132   // Put callee in place.
    133   __ LoadAccessor(callee, accessor_holder, accessor_index,
    134                   is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);
    135 
    136   // Put holder in place.
    137   CallOptimization::HolderLookup holder_lookup;
    138   int holder_depth = 0;
    139   optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup,
    140                                           &holder_depth);
    141   switch (holder_lookup) {
    142     case CallOptimization::kHolderIsReceiver:
    143       __ Move(holder, receiver);
    144       break;
    145     case CallOptimization::kHolderFound:
    146       __ movp(holder, FieldOperand(receiver, HeapObject::kMapOffset));
    147       __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset));
    148       for (int i = 1; i < holder_depth; i++) {
    149         __ movp(holder, FieldOperand(holder, HeapObject::kMapOffset));
    150         __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset));
    151       }
    152       break;
    153     case CallOptimization::kHolderNotFound:
    154       UNREACHABLE();
    155       break;
    156   }
    157 
    158   Isolate* isolate = masm->isolate();
    159   Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
    160   bool call_data_undefined = false;
    161   // Put call data in place.
    162   if (api_call_info->data()->IsUndefined(isolate)) {
    163     call_data_undefined = true;
    164     __ LoadRoot(data, Heap::kUndefinedValueRootIndex);
    165   } else {
    166     if (optimization.is_constant_call()) {
    167       __ movp(data,
    168               FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset));
    169       __ movp(data,
    170               FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset));
    171       __ movp(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset));
    172     } else {
    173       __ movp(data,
    174               FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
    175     }
    176     __ movp(data, FieldOperand(data, CallHandlerInfo::kDataOffset));
    177   }
    178 
    179   if (api_call_info->fast_handler()->IsCode()) {
    180     // Just tail call into the fast handler if present.
    181     __ Jump(handle(Code::cast(api_call_info->fast_handler())),
    182             RelocInfo::CODE_TARGET);
    183     return;
    184   }
    185 
    186   // Put api_function_address in place.
    187   Address function_address = v8::ToCData<Address>(api_call_info->callback());
    188   __ Move(api_function_address, function_address,
    189           RelocInfo::EXTERNAL_REFERENCE);
    190 
    191   // Jump to stub.
    192   CallApiCallbackStub stub(isolate, is_store, call_data_undefined,
    193                            !optimization.is_constant_call());
    194   __ TailCallStub(&stub);
    195 }
    196 
    197 
    198 void PropertyHandlerCompiler::GenerateCheckPropertyCell(
    199     MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
    200     Register scratch, Label* miss) {
    201   Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
    202       global, name, PropertyCellType::kInvalidated);
    203   Isolate* isolate = masm->isolate();
    204   DCHECK(cell->value()->IsTheHole(isolate));
    205   Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
    206   __ LoadWeakValue(scratch, weak_cell, miss);
    207   __ Cmp(FieldOperand(scratch, PropertyCell::kValueOffset),
    208          isolate->factory()->the_hole_value());
    209   __ j(not_equal, miss);
    210 }
    211 
    212 
    213 void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
    214     MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
    215     int accessor_index, int expected_arguments, Register scratch) {
    216   // ----------- S t a t e -------------
    217   //  -- rsp[0] : return address
    218   // -----------------------------------
    219   {
    220     FrameScope scope(masm, StackFrame::INTERNAL);
    221 
    222     // Save context register
    223     __ pushq(rsi);
    224     // Save value register, so we can restore it later.
    225     __ Push(value());
    226 
    227     if (accessor_index >= 0) {
    228       DCHECK(!holder.is(scratch));
    229       DCHECK(!receiver.is(scratch));
    230       DCHECK(!value().is(scratch));
    231       // Call the JavaScript setter with receiver and value on the stack.
    232       if (map->IsJSGlobalObjectMap()) {
    233         // Swap in the global receiver.
    234         __ movp(scratch,
    235                 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
    236         receiver = scratch;
    237       }
    238       __ Push(receiver);
    239       __ Push(value());
    240       __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_SETTER);
    241       __ Set(rax, 1);
    242       __ Call(masm->isolate()->builtins()->CallFunction(
    243                   ConvertReceiverMode::kNotNullOrUndefined),
    244               RelocInfo::CODE_TARGET);
    245     } else {
    246       // If we generate a global code snippet for deoptimization only, remember
    247       // the place to continue after deoptimization.
    248       masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
    249     }
    250 
    251     // We have to return the passed value, not the return value of the setter.
    252     __ Pop(rax);
    253 
    254     // Restore context register.
    255     __ popq(rsi);
    256   }
    257   __ ret(0);
    258 }
    259 
    260 
    261 void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
    262     MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
    263     int accessor_index, int expected_arguments, Register scratch) {
    264   // ----------- S t a t e -------------
    265   //  -- rax    : receiver
    266   //  -- rcx    : name
    267   //  -- rsp[0] : return address
    268   // -----------------------------------
    269   {
    270     FrameScope scope(masm, StackFrame::INTERNAL);
    271 
    272     // Save context register
    273     __ pushq(rsi);
    274 
    275     if (accessor_index >= 0) {
    276       DCHECK(!holder.is(scratch));
    277       DCHECK(!receiver.is(scratch));
    278       // Call the JavaScript getter with the receiver on the stack.
    279       if (map->IsJSGlobalObjectMap()) {
    280         // Swap in the global receiver.
    281         __ movp(scratch,
    282                 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
    283         receiver = scratch;
    284       }
    285       __ Push(receiver);
    286       __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_GETTER);
    287       __ Set(rax, 0);
    288       __ Call(masm->isolate()->builtins()->CallFunction(
    289                   ConvertReceiverMode::kNotNullOrUndefined),
    290               RelocInfo::CODE_TARGET);
    291     } else {
    292       // If we generate a global code snippet for deoptimization only, remember
    293       // the place to continue after deoptimization.
    294       masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
    295     }
    296 
    297     // Restore context register.
    298     __ popq(rsi);
    299   }
    300   __ ret(0);
    301 }
    302 
    303 #undef __
    304 #define __ ACCESS_MASM((masm()))
    305 
    306 
    307 void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
    308                                                     Handle<Name> name) {
    309   if (!label->is_unused()) {
    310     __ bind(label);
    311     __ Move(this->name(), name);
    312   }
    313 }
    314 
    315 void PropertyHandlerCompiler::GenerateAccessCheck(
    316     Handle<WeakCell> native_context_cell, Register scratch1, Register scratch2,
    317     Label* miss, bool compare_native_contexts_only) {
    318   Label done;
    319   // Load current native context.
    320   __ movp(scratch1, NativeContextOperand());
    321   // Load expected native context.
    322   __ LoadWeakValue(scratch2, native_context_cell, miss);
    323   __ cmpp(scratch1, scratch2);
    324 
    325   if (!compare_native_contexts_only) {
    326     __ j(equal, &done);
    327 
    328     // Compare security tokens of current and expected native contexts.
    329     __ movp(scratch1, ContextOperand(scratch1, Context::SECURITY_TOKEN_INDEX));
    330     __ movp(scratch2, ContextOperand(scratch2, Context::SECURITY_TOKEN_INDEX));
    331     __ cmpp(scratch1, scratch2);
    332   }
    333   __ j(not_equal, miss);
    334 
    335   __ bind(&done);
    336 }
    337 
    338 Register PropertyHandlerCompiler::CheckPrototypes(
    339     Register object_reg, Register holder_reg, Register scratch1,
    340     Register scratch2, Handle<Name> name, Label* miss,
    341     ReturnHolder return_what) {
    342   Handle<Map> receiver_map = map();
    343 
    344   // Make sure there's no overlap between holder and object registers.
    345   DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
    346   DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
    347          !scratch2.is(scratch1));
    348 
    349   Handle<Cell> validity_cell =
    350       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
    351   if (!validity_cell.is_null()) {
    352     DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value());
    353     __ Move(scratch1, validity_cell, RelocInfo::CELL);
    354     // Move(..., CELL) loads the payload's address!
    355     __ SmiCompare(Operand(scratch1, 0),
    356                   Smi::FromInt(Map::kPrototypeChainValid));
    357     __ j(not_equal, miss);
    358   }
    359 
    360   // Keep track of the current object in register reg.  On the first
    361   // iteration, reg is an alias for object_reg, on later iterations,
    362   // it is an alias for holder_reg.
    363   Register reg = object_reg;
    364   int depth = 0;
    365 
    366   Handle<JSObject> current = Handle<JSObject>::null();
    367   if (receiver_map->IsJSGlobalObjectMap()) {
    368     current = isolate()->global_object();
    369   }
    370 
    371   Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()),
    372                           isolate());
    373   Handle<Map> holder_map(holder()->map());
    374   // Traverse the prototype chain and check the maps in the prototype chain for
    375   // fast and global objects or do negative lookup for normal objects.
    376   while (!current_map.is_identical_to(holder_map)) {
    377     ++depth;
    378 
    379     if (current_map->IsJSGlobalObjectMap()) {
    380       GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
    381                                 name, scratch2, miss);
    382     } else if (current_map->is_dictionary_map()) {
    383       DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
    384       DCHECK(name->IsUniqueName());
    385       DCHECK(current.is_null() ||
    386              current->property_dictionary()->FindEntry(name) ==
    387                  NameDictionary::kNotFound);
    388 
    389       if (depth > 1) {
    390         Handle<WeakCell> weak_cell =
    391             Map::GetOrCreatePrototypeWeakCell(current, isolate());
    392         __ LoadWeakValue(reg, weak_cell, miss);
    393       }
    394       GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
    395                                        scratch2);
    396     }
    397 
    398     reg = holder_reg;  // From now on the object will be in holder_reg.
    399     // Go to the next object in the prototype chain.
    400     current = handle(JSObject::cast(current_map->prototype()));
    401     current_map = handle(current->map());
    402   }
    403 
    404   DCHECK(!current_map->IsJSGlobalProxyMap());
    405 
    406   // Log the check depth.
    407   LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
    408 
    409   bool return_holder = return_what == RETURN_HOLDER;
    410   if (return_holder && depth != 0) {
    411     Handle<WeakCell> weak_cell =
    412         Map::GetOrCreatePrototypeWeakCell(current, isolate());
    413     __ LoadWeakValue(reg, weak_cell, miss);
    414   }
    415 
    416   // Return the register containing the holder.
    417   return return_holder ? reg : no_reg;
    418 }
    419 
    420 
    421 void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
    422   if (!miss->is_unused()) {
    423     Label success;
    424     __ jmp(&success);
    425     __ bind(miss);
    426     if (IC::ICUseVector(kind())) {
    427       DCHECK(kind() == Code::LOAD_IC);
    428       PopVectorAndSlot();
    429     }
    430     TailCallBuiltin(masm(), MissBuiltin(kind()));
    431     __ bind(&success);
    432   }
    433 }
    434 
    435 
    436 void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
    437   if (!miss->is_unused()) {
    438     Label success;
    439     __ jmp(&success);
    440     GenerateRestoreName(miss, name);
    441     if (IC::ICUseVector(kind())) PopVectorAndSlot();
    442     TailCallBuiltin(masm(), MissBuiltin(kind()));
    443     __ bind(&success);
    444   }
    445 }
    446 
    447 void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
    448     LookupIterator* it, Register holder_reg) {
    449   DCHECK(holder()->HasNamedInterceptor());
    450   DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
    451 
    452   // Compile the interceptor call, followed by inline code to load the
    453   // property from further up the prototype chain if the call fails.
    454   // Check that the maps haven't changed.
    455   DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
    456 
    457   // Preserve the receiver register explicitly whenever it is different from the
    458   // holder and it is needed should the interceptor return without any result.
    459   // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
    460   // case might cause a miss during the prototype check.
    461   bool must_perform_prototype_check =
    462       !holder().is_identical_to(it->GetHolder<JSObject>());
    463   bool must_preserve_receiver_reg =
    464       !receiver().is(holder_reg) &&
    465       (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
    466 
    467   // Save necessary data before invoking an interceptor.
    468   // Requires a frame to make GC aware of pushed pointers.
    469   {
    470     FrameScope frame_scope(masm(), StackFrame::INTERNAL);
    471 
    472     if (must_preserve_receiver_reg) {
    473       __ Push(receiver());
    474     }
    475     __ Push(holder_reg);
    476     __ Push(this->name());
    477     InterceptorVectorSlotPush(holder_reg);
    478 
    479     // Invoke an interceptor.  Note: map checks from receiver to
    480     // interceptor's holder has been compiled before (see a caller
    481     // of this method.)
    482     CompileCallLoadPropertyWithInterceptor(
    483         masm(), receiver(), holder_reg, this->name(), holder(),
    484         Runtime::kLoadPropertyWithInterceptorOnly);
    485 
    486     // Check if interceptor provided a value for property.  If it's
    487     // the case, return immediately.
    488     Label interceptor_failed;
    489     __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
    490     __ j(equal, &interceptor_failed);
    491     frame_scope.GenerateLeaveFrame();
    492     __ ret(0);
    493 
    494     __ bind(&interceptor_failed);
    495     InterceptorVectorSlotPop(holder_reg);
    496     __ Pop(this->name());
    497     __ Pop(holder_reg);
    498     if (must_preserve_receiver_reg) {
    499       __ Pop(receiver());
    500     }
    501 
    502     // Leave the internal frame.
    503   }
    504 
    505   GenerateLoadPostInterceptor(it, holder_reg);
    506 }
    507 
    508 
    509 void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
    510   // Call the runtime system to load the interceptor.
    511   DCHECK(holder()->HasNamedInterceptor());
    512   DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
    513 
    514   // Stack:
    515   //   return address
    516 
    517   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
    518   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
    519   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
    520   STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
    521   __ Push(receiver());
    522   __ Push(holder_reg);
    523   // See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
    524   if (holder_reg.is(receiver())) {
    525     __ Push(slot());
    526     __ Push(vector());
    527   } else {
    528     __ Push(scratch3());  // slot
    529     __ Push(scratch2());  // vector
    530   }
    531   __ Push(Operand(rsp, 4 * kPointerSize));  // return address
    532   __ movp(Operand(rsp, 5 * kPointerSize), name());
    533 
    534   __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
    535 }
    536 
    537 void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
    538   STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
    539 }
    540 
    541 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
    542     Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
    543     LanguageMode language_mode) {
    544   Register holder_reg = Frontend(name);
    545 
    546   __ PopReturnAddressTo(scratch1());
    547   __ Push(receiver());
    548   __ Push(holder_reg);
    549   // If the callback cannot leak, then push the callback directly,
    550   // otherwise wrap it in a weak cell.
    551   if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) {
    552     __ Push(callback);
    553   } else {
    554     Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
    555     __ Push(cell);
    556   }
    557   __ Push(name);
    558   __ Push(value());
    559   __ Push(Smi::FromInt(language_mode));
    560   __ PushReturnAddressFrom(scratch1());
    561 
    562   // Do tail-call to the runtime system.
    563   __ TailCallRuntime(Runtime::kStoreCallbackProperty);
    564 
    565   // Return the generated code.
    566   return GetCode(kind(), name);
    567 }
    568 
    569 
    570 Register NamedStoreHandlerCompiler::value() {
    571   return StoreDescriptor::ValueRegister();
    572 }
    573 
    574 
    575 Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
    576     Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
    577   Label miss;
    578   if (IC::ICUseVector(kind())) {
    579     PushVectorAndSlot();
    580   }
    581   FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
    582 
    583   // Get the value from the cell.
    584   Register result = StoreDescriptor::ValueRegister();
    585   Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
    586   __ LoadWeakValue(result, weak_cell, &miss);
    587   __ movp(result, FieldOperand(result, PropertyCell::kValueOffset));
    588 
    589   // Check for deleted property if property can actually be deleted.
    590   if (is_configurable) {
    591     __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
    592     __ j(equal, &miss);
    593   } else if (FLAG_debug_code) {
    594     __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
    595     __ Check(not_equal, kDontDeleteCellsCannotContainTheHole);
    596   }
    597 
    598   Counters* counters = isolate()->counters();
    599   __ IncrementCounter(counters->ic_named_load_global_stub(), 1);
    600   if (IC::ICUseVector(kind())) {
    601     DiscardVectorAndSlot();
    602   }
    603   __ ret(0);
    604 
    605   FrontendFooter(name, &miss);
    606 
    607   // Return the generated code.
    608   return GetCode(kind(), name);
    609 }
    610 
    611 
    612 #undef __
    613 }  // namespace internal
    614 }  // namespace v8
    615 
    616 #endif  // V8_TARGET_ARCH_X64
    617