Home | History | Annotate | Download | only in ic
      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 #include "src/ic/handler-compiler.h"
      6 
      7 #include "src/field-type.h"
      8 #include "src/ic/call-optimization.h"
      9 #include "src/ic/handler-configuration-inl.h"
     10 #include "src/ic/ic-inl.h"
     11 #include "src/ic/ic.h"
     12 #include "src/isolate-inl.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
     18                                            Handle<Map> stub_holder,
     19                                            Code::Kind kind,
     20                                            CacheHolderFlag cache_holder) {
     21   Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder);
     22   Code* code = stub_holder->LookupInCodeCache(*name, flags);
     23   if (code == nullptr) return Handle<Code>();
     24   return handle(code);
     25 }
     26 
     27 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
     28                                               Handle<Name> name) {
     29   Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder());
     30   Handle<Code> code = GetCodeWithFlags(flags, name);
     31   PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG,
     32                                      AbstractCode::cast(*code), *name));
     33 #ifdef DEBUG
     34   code->VerifyEmbeddedObjects();
     35 #endif
     36   return code;
     37 }
     38 
     39 
     40 #define __ ACCESS_MASM(masm())
     41 
     42 
     43 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
     44                                                   Handle<Name> name,
     45                                                   Label* miss,
     46                                                   ReturnHolder return_what) {
     47   if (map()->IsPrimitiveMap() || map()->IsJSGlobalProxyMap()) {
     48     // If the receiver is a global proxy and if we get to this point then
     49     // the compile-time (current) native context has access to global proxy's
     50     // native context. Since access rights revocation is not supported at all,
     51     // we can generate a check that an execution-time native context is either
     52     // the same as compile-time native context or has the same access token.
     53     Handle<Context> native_context = isolate()->native_context();
     54     Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
     55 
     56     bool compare_native_contexts_only = map()->IsPrimitiveMap();
     57     GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss,
     58                         compare_native_contexts_only);
     59   }
     60 
     61   // Check that the maps starting from the prototype haven't changed.
     62   return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
     63                          miss, return_what);
     64 }
     65 
     66 
     67 // Frontend for store uses the name register. It has to be restored before a
     68 // miss.
     69 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
     70                                                    Handle<Name> name,
     71                                                    Label* miss,
     72                                                    ReturnHolder return_what) {
     73   if (map()->IsJSGlobalProxyMap()) {
     74     Handle<Context> native_context = isolate()->native_context();
     75     Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
     76     GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, false);
     77   }
     78 
     79   return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
     80                          miss, return_what);
     81 }
     82 
     83 
     84 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
     85   Label miss;
     86   if (IC::ShouldPushPopSlotAndVector(kind())) {
     87     PushVectorAndSlot();
     88   }
     89   Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
     90   FrontendFooter(name, &miss);
     91   // The footer consumes the vector and slot from the stack if miss occurs.
     92   if (IC::ShouldPushPopSlotAndVector(kind())) {
     93     DiscardVectorAndSlot();
     94   }
     95   return reg;
     96 }
     97 
     98 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
     99     Handle<Name> name, Handle<AccessorInfo> callback, Handle<Code> slow_stub) {
    100   if (V8_UNLIKELY(FLAG_runtime_stats)) {
    101     GenerateTailCall(masm(), slow_stub);
    102   }
    103   Register reg = Frontend(name);
    104   GenerateLoadCallback(reg, callback);
    105   return GetCode(kind(), name);
    106 }
    107 
    108 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
    109     Handle<Name> name, const CallOptimization& call_optimization,
    110     int accessor_index, Handle<Code> slow_stub) {
    111   DCHECK(call_optimization.is_simple_api_call());
    112   if (V8_UNLIKELY(FLAG_runtime_stats)) {
    113     GenerateTailCall(masm(), slow_stub);
    114   }
    115   Register holder = Frontend(name);
    116   GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(),
    117                           scratch2(), false, no_reg, holder, accessor_index);
    118   return GetCode(kind(), name);
    119 }
    120 
    121 
    122 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
    123   if (IC::ShouldPushPopSlotAndVector(kind())) {
    124     if (holder_reg.is(receiver())) {
    125       PushVectorAndSlot();
    126     } else {
    127       DCHECK(holder_reg.is(scratch1()));
    128       PushVectorAndSlot(scratch2(), scratch3());
    129     }
    130   }
    131 }
    132 
    133 
    134 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
    135                                                         PopMode mode) {
    136   if (IC::ShouldPushPopSlotAndVector(kind())) {
    137     if (mode == DISCARD) {
    138       DiscardVectorAndSlot();
    139     } else {
    140       if (holder_reg.is(receiver())) {
    141         PopVectorAndSlot();
    142       } else {
    143         DCHECK(holder_reg.is(scratch1()));
    144         PopVectorAndSlot(scratch2(), scratch3());
    145       }
    146     }
    147   }
    148 }
    149 
    150 
    151 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
    152     LookupIterator* it) {
    153   // So far the most popular follow ups for interceptor loads are DATA and
    154   // AccessorInfo, so inline only them. Other cases may be added
    155   // later.
    156   bool inline_followup = false;
    157   switch (it->state()) {
    158     case LookupIterator::TRANSITION:
    159       UNREACHABLE();
    160     case LookupIterator::ACCESS_CHECK:
    161     case LookupIterator::INTERCEPTOR:
    162     case LookupIterator::JSPROXY:
    163     case LookupIterator::NOT_FOUND:
    164     case LookupIterator::INTEGER_INDEXED_EXOTIC:
    165       break;
    166     case LookupIterator::DATA: {
    167       PropertyDetails details = it->property_details();
    168       inline_followup = details.kind() == kData &&
    169                         details.location() == kField &&
    170                         !it->is_dictionary_holder();
    171       break;
    172     }
    173     case LookupIterator::ACCESSOR: {
    174       Handle<Object> accessors = it->GetAccessors();
    175       if (accessors->IsAccessorInfo()) {
    176         Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
    177         inline_followup =
    178             info->getter() != NULL &&
    179             AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map());
    180       } else if (accessors->IsAccessorPair()) {
    181         Handle<JSObject> property_holder(it->GetHolder<JSObject>());
    182         Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
    183                               isolate());
    184         if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) {
    185           break;
    186         }
    187         if (!property_holder->HasFastProperties()) break;
    188         CallOptimization call_optimization(getter);
    189         Handle<Map> receiver_map = map();
    190         inline_followup = call_optimization.is_simple_api_call() &&
    191                           call_optimization.IsCompatibleReceiverMap(
    192                               receiver_map, property_holder);
    193       }
    194     }
    195   }
    196 
    197   Label miss;
    198   InterceptorVectorSlotPush(receiver());
    199   bool lost_holder_register = false;
    200   auto holder_orig = holder();
    201   // non masking interceptors must check the entire chain, so temporarily reset
    202   // the holder to be that last element for the FrontendHeader call.
    203   if (holder()->GetNamedInterceptor()->non_masking()) {
    204     DCHECK(!inline_followup);
    205     JSObject* last = *holder();
    206     PrototypeIterator iter(isolate(), last);
    207     while (!iter.IsAtEnd()) {
    208       lost_holder_register = true;
    209       // Casting to JSObject is fine here. The LookupIterator makes sure to
    210       // look behind non-masking interceptors during the original lookup, and
    211       // we wouldn't try to compile a handler if there was a Proxy anywhere.
    212       last = iter.GetCurrent<JSObject>();
    213       iter.Advance();
    214     }
    215     auto last_handle = handle(last);
    216     set_holder(last_handle);
    217   }
    218   Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
    219   // Reset the holder so further calculations are correct.
    220   set_holder(holder_orig);
    221   if (lost_holder_register) {
    222     if (*it->GetReceiver() == *holder()) {
    223       reg = receiver();
    224     } else {
    225       // Reload lost holder register.
    226       auto cell = isolate()->factory()->NewWeakCell(holder());
    227       __ LoadWeakValue(reg, cell, &miss);
    228     }
    229   }
    230   FrontendFooter(it->name(), &miss);
    231   InterceptorVectorSlotPop(reg);
    232   if (inline_followup) {
    233     // TODO(368): Compile in the whole chain: all the interceptors in
    234     // prototypes and ultimate answer.
    235     GenerateLoadInterceptorWithFollowup(it, reg);
    236   } else {
    237     GenerateLoadInterceptor(reg);
    238   }
    239   return GetCode(kind(), it->name());
    240 }
    241 
    242 void NamedLoadHandlerCompiler::GenerateLoadCallback(
    243     Register reg, Handle<AccessorInfo> callback) {
    244   DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
    245   __ Move(ApiGetterDescriptor::HolderRegister(), reg);
    246   // The callback is alive if this instruction is executed,
    247   // so the weak cell is not cleared and points to data.
    248   Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
    249   __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
    250 
    251   CallApiGetterStub stub(isolate());
    252   __ TailCallStub(&stub);
    253 }
    254 
    255 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
    256     LookupIterator* it, Register interceptor_reg) {
    257   Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
    258 
    259   Handle<Map> holder_map(holder()->map());
    260   set_map(holder_map);
    261   set_holder(real_named_property_holder);
    262 
    263   Label miss;
    264   InterceptorVectorSlotPush(interceptor_reg);
    265   Register reg =
    266       FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
    267   FrontendFooter(it->name(), &miss);
    268   // We discard the vector and slot now because we don't miss below this point.
    269   InterceptorVectorSlotPop(reg, DISCARD);
    270 
    271   switch (it->state()) {
    272     case LookupIterator::ACCESS_CHECK:
    273     case LookupIterator::INTERCEPTOR:
    274     case LookupIterator::JSPROXY:
    275     case LookupIterator::NOT_FOUND:
    276     case LookupIterator::INTEGER_INDEXED_EXOTIC:
    277     case LookupIterator::TRANSITION:
    278       UNREACHABLE();
    279     case LookupIterator::DATA: {
    280       DCHECK_EQ(kData, it->property_details().kind());
    281       DCHECK_EQ(kField, it->property_details().location());
    282       __ Move(LoadFieldDescriptor::ReceiverRegister(), reg);
    283       Handle<Object> smi_handler =
    284           LoadIC::SimpleFieldLoad(isolate(), it->GetFieldIndex());
    285       __ Move(LoadFieldDescriptor::SmiHandlerRegister(), smi_handler);
    286       GenerateTailCall(masm(), isolate()->builtins()->LoadField());
    287       break;
    288     }
    289     case LookupIterator::ACCESSOR:
    290       if (it->GetAccessors()->IsAccessorInfo()) {
    291         Handle<AccessorInfo> info =
    292             Handle<AccessorInfo>::cast(it->GetAccessors());
    293         DCHECK_NOT_NULL(info->getter());
    294         GenerateLoadCallback(reg, info);
    295       } else {
    296         Handle<Object> function = handle(
    297             AccessorPair::cast(*it->GetAccessors())->getter(), isolate());
    298         CallOptimization call_optimization(function);
    299         GenerateApiAccessorCall(masm(), call_optimization, holder_map,
    300                                 receiver(), scratch2(), false, no_reg, reg,
    301                                 it->GetAccessorIndex());
    302       }
    303   }
    304 }
    305 
    306 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
    307     Handle<Name> name, int accessor_index, int expected_arguments) {
    308   Register holder = Frontend(name);
    309   GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index,
    310                         expected_arguments, scratch2());
    311   return GetCode(kind(), name);
    312 }
    313 
    314 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
    315     Handle<JSObject> object, Handle<Name> name, int accessor_index,
    316     int expected_arguments) {
    317   Register holder = Frontend(name);
    318   GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index,
    319                          expected_arguments, scratch2());
    320 
    321   return GetCode(kind(), name);
    322 }
    323 
    324 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
    325     Handle<JSObject> object, Handle<Name> name,
    326     const CallOptimization& call_optimization, int accessor_index,
    327     Handle<Code> slow_stub) {
    328   if (V8_UNLIKELY(FLAG_runtime_stats)) {
    329     GenerateTailCall(masm(), slow_stub);
    330   }
    331   Register holder = Frontend(name);
    332   if (Descriptor::kPassLastArgsOnStack) {
    333     __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
    334   }
    335   GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()),
    336                           receiver(), scratch2(), true, value(), holder,
    337                           accessor_index);
    338   return GetCode(kind(), name);
    339 }
    340 
    341 
    342 #undef __
    343 
    344 // static
    345 Handle<Object> ElementHandlerCompiler::GetKeyedLoadHandler(
    346     Handle<Map> receiver_map, Isolate* isolate) {
    347   if (receiver_map->has_indexed_interceptor() &&
    348       !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(isolate) &&
    349       !receiver_map->GetIndexedInterceptor()->non_masking()) {
    350     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedInterceptorStub);
    351     return LoadIndexedInterceptorStub(isolate).GetCode();
    352   }
    353   if (receiver_map->IsStringMap()) {
    354     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedStringStub);
    355     return isolate->builtins()->KeyedLoadIC_IndexedString();
    356   }
    357   InstanceType instance_type = receiver_map->instance_type();
    358   if (instance_type < FIRST_JS_RECEIVER_TYPE) {
    359     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_SlowStub);
    360     return isolate->builtins()->KeyedLoadIC_Slow();
    361   }
    362 
    363   ElementsKind elements_kind = receiver_map->elements_kind();
    364   if (IsSloppyArgumentsElements(elements_kind)) {
    365     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_KeyedLoadSloppyArgumentsStub);
    366     return KeyedLoadSloppyArgumentsStub(isolate).GetCode();
    367   }
    368   bool is_js_array = instance_type == JS_ARRAY_TYPE;
    369   if (elements_kind == DICTIONARY_ELEMENTS) {
    370     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
    371     return LoadHandler::LoadElement(isolate, elements_kind, false, is_js_array);
    372   }
    373   DCHECK(IsFastElementsKind(elements_kind) ||
    374          IsFixedTypedArrayElementsKind(elements_kind));
    375   // TODO(jkummerow): Use IsHoleyElementsKind(elements_kind).
    376   bool convert_hole_to_undefined =
    377       is_js_array && elements_kind == FAST_HOLEY_ELEMENTS &&
    378       *receiver_map == isolate->get_initial_js_array_map(elements_kind);
    379   TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
    380   return LoadHandler::LoadElement(isolate, elements_kind,
    381                                   convert_hole_to_undefined, is_js_array);
    382 }
    383 
    384 void ElementHandlerCompiler::CompileElementHandlers(
    385     MapHandleList* receiver_maps, List<Handle<Object>>* handlers) {
    386   for (int i = 0; i < receiver_maps->length(); ++i) {
    387     handlers->Add(GetKeyedLoadHandler(receiver_maps->at(i), isolate()));
    388   }
    389 }
    390 }  // namespace internal
    391 }  // namespace v8
    392