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/v8.h"
      6 
      7 #include "src/ic/call-optimization.h"
      8 #include "src/ic/handler-compiler.h"
      9 #include "src/ic/ic.h"
     10 #include "src/ic/ic-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 
     16 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
     17                                            Handle<Map> stub_holder,
     18                                            Code::Kind kind,
     19                                            CacheHolderFlag cache_holder,
     20                                            Code::StubType type) {
     21   Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder);
     22   Object* probe = stub_holder->FindInCodeCache(*name, flags);
     23   if (probe->IsCode()) return handle(Code::cast(probe));
     24   return Handle<Code>::null();
     25 }
     26 
     27 
     28 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent(
     29     Handle<Name> name, Handle<HeapType> type) {
     30   Isolate* isolate = name->GetIsolate();
     31   Handle<Map> receiver_map = IC::TypeToMap(*type, isolate);
     32   if (receiver_map->prototype()->IsNull()) {
     33     // TODO(jkummerow/verwaest): If there is no prototype and the property
     34     // is nonexistent, introduce a builtin to handle this (fast properties
     35     // -> return undefined, dictionary properties -> do negative lookup).
     36     return Handle<Code>();
     37   }
     38   CacheHolderFlag flag;
     39   Handle<Map> stub_holder_map =
     40       IC::GetHandlerCacheHolder(*type, false, isolate, &flag);
     41 
     42   // If no dictionary mode objects are present in the prototype chain, the load
     43   // nonexistent IC stub can be shared for all names for a given map and we use
     44   // the empty string for the map cache in that case. If there are dictionary
     45   // mode objects involved, we need to do negative lookups in the stub and
     46   // therefore the stub will be specific to the name.
     47   Handle<Name> cache_name =
     48       receiver_map->is_dictionary_map()
     49           ? name
     50           : Handle<Name>::cast(isolate->factory()->nonexistent_symbol());
     51   Handle<Map> current_map = stub_holder_map;
     52   Handle<JSObject> last(JSObject::cast(receiver_map->prototype()));
     53   while (true) {
     54     if (current_map->is_dictionary_map()) cache_name = name;
     55     if (current_map->prototype()->IsNull()) break;
     56     last = handle(JSObject::cast(current_map->prototype()));
     57     current_map = handle(last->map());
     58   }
     59   // Compile the stub that is either shared for all names or
     60   // name specific if there are global objects involved.
     61   Handle<Code> handler = PropertyHandlerCompiler::Find(
     62       cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST);
     63   if (!handler.is_null()) return handler;
     64 
     65   NamedLoadHandlerCompiler compiler(isolate, type, last, flag);
     66   handler = compiler.CompileLoadNonexistent(cache_name);
     67   Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
     68   return handler;
     69 }
     70 
     71 
     72 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
     73                                               Code::StubType type,
     74                                               Handle<Name> name) {
     75   Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder());
     76   Handle<Code> code = GetCodeWithFlags(flags, name);
     77   PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name));
     78   return code;
     79 }
     80 
     81 
     82 void PropertyHandlerCompiler::set_type_for_object(Handle<Object> object) {
     83   type_ = IC::CurrentTypeOf(object, isolate());
     84 }
     85 
     86 
     87 #define __ ACCESS_MASM(masm())
     88 
     89 
     90 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
     91                                                   Handle<Name> name,
     92                                                   Label* miss) {
     93   PrototypeCheckType check_type = CHECK_ALL_MAPS;
     94   int function_index = -1;
     95   if (type()->Is(HeapType::String())) {
     96     function_index = Context::STRING_FUNCTION_INDEX;
     97   } else if (type()->Is(HeapType::Symbol())) {
     98     function_index = Context::SYMBOL_FUNCTION_INDEX;
     99   } else if (type()->Is(HeapType::Number())) {
    100     function_index = Context::NUMBER_FUNCTION_INDEX;
    101   } else if (type()->Is(HeapType::Boolean())) {
    102     function_index = Context::BOOLEAN_FUNCTION_INDEX;
    103   } else {
    104     check_type = SKIP_RECEIVER;
    105   }
    106 
    107   if (check_type == CHECK_ALL_MAPS) {
    108     GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index,
    109                                               scratch1(), miss);
    110     Object* function = isolate()->native_context()->get(function_index);
    111     Object* prototype = JSFunction::cast(function)->instance_prototype();
    112     set_type_for_object(handle(prototype, isolate()));
    113     object_reg = scratch1();
    114   }
    115 
    116   // Check that the maps starting from the prototype haven't changed.
    117   return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
    118                          miss, check_type);
    119 }
    120 
    121 
    122 // Frontend for store uses the name register. It has to be restored before a
    123 // miss.
    124 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
    125                                                    Handle<Name> name,
    126                                                    Label* miss) {
    127   return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
    128                          miss, SKIP_RECEIVER);
    129 }
    130 
    131 
    132 Register PropertyHandlerCompiler::Frontend(Register object_reg,
    133                                            Handle<Name> name) {
    134   Label miss;
    135   Register reg = FrontendHeader(object_reg, name, &miss);
    136   FrontendFooter(name, &miss);
    137   return reg;
    138 }
    139 
    140 
    141 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
    142                                                         Label* miss,
    143                                                         Register scratch1,
    144                                                         Register scratch2) {
    145   Register holder_reg;
    146   Handle<Map> last_map;
    147   if (holder().is_null()) {
    148     holder_reg = receiver();
    149     last_map = IC::TypeToMap(*type(), isolate());
    150     // If |type| has null as its prototype, |holder()| is
    151     // Handle<JSObject>::null().
    152     DCHECK(last_map->prototype() == isolate()->heap()->null_value());
    153   } else {
    154     holder_reg = FrontendHeader(receiver(), name, miss);
    155     last_map = handle(holder()->map());
    156   }
    157 
    158   if (last_map->is_dictionary_map()) {
    159     if (last_map->IsJSGlobalObjectMap()) {
    160       Handle<JSGlobalObject> global =
    161           holder().is_null()
    162               ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value())
    163               : Handle<JSGlobalObject>::cast(holder());
    164       GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
    165     } else {
    166       if (!name->IsUniqueName()) {
    167         DCHECK(name->IsString());
    168         name = factory()->InternalizeString(Handle<String>::cast(name));
    169       }
    170       DCHECK(holder().is_null() ||
    171              holder()->property_dictionary()->FindEntry(name) ==
    172                  NameDictionary::kNotFound);
    173       GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
    174                                        scratch2);
    175     }
    176   }
    177 }
    178 
    179 
    180 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
    181                                                         FieldIndex field) {
    182   Register reg = Frontend(receiver(), name);
    183   __ Move(receiver(), reg);
    184   LoadFieldStub stub(isolate(), field);
    185   GenerateTailCall(masm(), stub.GetCode());
    186   return GetCode(kind(), Code::FAST, name);
    187 }
    188 
    189 
    190 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name,
    191                                                            int constant_index) {
    192   Register reg = Frontend(receiver(), name);
    193   __ Move(receiver(), reg);
    194   LoadConstantStub stub(isolate(), constant_index);
    195   GenerateTailCall(masm(), stub.GetCode());
    196   return GetCode(kind(), Code::FAST, name);
    197 }
    198 
    199 
    200 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
    201     Handle<Name> name) {
    202   Label miss;
    203   NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
    204   GenerateLoadConstant(isolate()->factory()->undefined_value());
    205   FrontendFooter(name, &miss);
    206   return GetCode(kind(), Code::FAST, name);
    207 }
    208 
    209 
    210 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
    211     Handle<Name> name, Handle<ExecutableAccessorInfo> callback) {
    212   Register reg = Frontend(receiver(), name);
    213   GenerateLoadCallback(reg, callback);
    214   return GetCode(kind(), Code::FAST, name);
    215 }
    216 
    217 
    218 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
    219     Handle<Name> name, const CallOptimization& call_optimization) {
    220   DCHECK(call_optimization.is_simple_api_call());
    221   Frontend(receiver(), name);
    222   Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate());
    223   GenerateFastApiCall(masm(), call_optimization, receiver_map, receiver(),
    224                       scratch1(), false, 0, NULL);
    225   return GetCode(kind(), Code::FAST, name);
    226 }
    227 
    228 
    229 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
    230     LookupIterator* it) {
    231   // So far the most popular follow ups for interceptor loads are FIELD and
    232   // ExecutableAccessorInfo, so inline only them. Other cases may be added
    233   // later.
    234   bool inline_followup = false;
    235   switch (it->state()) {
    236     case LookupIterator::TRANSITION:
    237       UNREACHABLE();
    238     case LookupIterator::ACCESS_CHECK:
    239     case LookupIterator::INTERCEPTOR:
    240     case LookupIterator::JSPROXY:
    241     case LookupIterator::NOT_FOUND:
    242       break;
    243     case LookupIterator::DATA:
    244       inline_followup = it->property_details().type() == FIELD;
    245       break;
    246     case LookupIterator::ACCESSOR: {
    247       Handle<Object> accessors = it->GetAccessors();
    248       inline_followup = accessors->IsExecutableAccessorInfo();
    249       if (!inline_followup) break;
    250       Handle<ExecutableAccessorInfo> info =
    251           Handle<ExecutableAccessorInfo>::cast(accessors);
    252       inline_followup = info->getter() != NULL &&
    253                         ExecutableAccessorInfo::IsCompatibleReceiverType(
    254                             isolate(), info, type());
    255     }
    256   }
    257 
    258   Register reg = Frontend(receiver(), it->name());
    259   if (inline_followup) {
    260     // TODO(368): Compile in the whole chain: all the interceptors in
    261     // prototypes and ultimate answer.
    262     GenerateLoadInterceptorWithFollowup(it, reg);
    263   } else {
    264     GenerateLoadInterceptor(reg);
    265   }
    266   return GetCode(kind(), Code::FAST, it->name());
    267 }
    268 
    269 
    270 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
    271     LookupIterator* it, Register interceptor_reg) {
    272   Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
    273 
    274   set_type_for_object(holder());
    275   set_holder(real_named_property_holder);
    276   Register reg = Frontend(interceptor_reg, it->name());
    277 
    278   switch (it->state()) {
    279     case LookupIterator::ACCESS_CHECK:
    280     case LookupIterator::INTERCEPTOR:
    281     case LookupIterator::JSPROXY:
    282     case LookupIterator::NOT_FOUND:
    283     case LookupIterator::TRANSITION:
    284       UNREACHABLE();
    285     case LookupIterator::DATA: {
    286       DCHECK_EQ(FIELD, it->property_details().type());
    287       __ Move(receiver(), reg);
    288       LoadFieldStub stub(isolate(), it->GetFieldIndex());
    289       GenerateTailCall(masm(), stub.GetCode());
    290       break;
    291     }
    292     case LookupIterator::ACCESSOR:
    293       Handle<ExecutableAccessorInfo> info =
    294           Handle<ExecutableAccessorInfo>::cast(it->GetAccessors());
    295       DCHECK_NE(NULL, info->getter());
    296       GenerateLoadCallback(reg, info);
    297   }
    298 }
    299 
    300 
    301 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
    302     Handle<Name> name, Handle<JSFunction> getter) {
    303   Frontend(receiver(), name);
    304   GenerateLoadViaGetter(masm(), type(), receiver(), getter);
    305   return GetCode(kind(), Code::FAST, name);
    306 }
    307 
    308 
    309 // TODO(verwaest): Cleanup. holder() is actually the receiver.
    310 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
    311     Handle<Map> transition, Handle<Name> name) {
    312   Label miss, slow;
    313 
    314   // Ensure no transitions to deprecated maps are followed.
    315   __ CheckMapDeprecated(transition, scratch1(), &miss);
    316 
    317   // Check that we are allowed to write this.
    318   bool is_nonexistent = holder()->map() == transition->GetBackPointer();
    319   if (is_nonexistent) {
    320     // Find the top object.
    321     Handle<JSObject> last;
    322     PrototypeIterator iter(isolate(), holder());
    323     while (!iter.IsAtEnd()) {
    324       last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
    325       iter.Advance();
    326     }
    327     if (!last.is_null()) set_holder(last);
    328     NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
    329   } else {
    330     FrontendHeader(receiver(), name, &miss);
    331     DCHECK(holder()->HasFastProperties());
    332   }
    333 
    334   GenerateStoreTransition(transition, name, receiver(), this->name(), value(),
    335                           scratch1(), scratch2(), scratch3(), &miss, &slow);
    336 
    337   GenerateRestoreName(&miss, name);
    338   TailCallBuiltin(masm(), MissBuiltin(kind()));
    339 
    340   GenerateRestoreName(&slow, name);
    341   TailCallBuiltin(masm(), SlowBuiltin(kind()));
    342   return GetCode(kind(), Code::FAST, name);
    343 }
    344 
    345 
    346 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
    347   Label miss;
    348   GenerateStoreField(it, value(), &miss);
    349   __ bind(&miss);
    350   TailCallBuiltin(masm(), MissBuiltin(kind()));
    351   return GetCode(kind(), Code::FAST, it->name());
    352 }
    353 
    354 
    355 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
    356     Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) {
    357   Frontend(receiver(), name);
    358   GenerateStoreViaSetter(masm(), type(), receiver(), setter);
    359 
    360   return GetCode(kind(), Code::FAST, name);
    361 }
    362 
    363 
    364 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
    365     Handle<JSObject> object, Handle<Name> name,
    366     const CallOptimization& call_optimization) {
    367   Frontend(receiver(), name);
    368   Register values[] = {value()};
    369   GenerateFastApiCall(masm(), call_optimization, handle(object->map()),
    370                       receiver(), scratch1(), true, 1, values);
    371   return GetCode(kind(), Code::FAST, name);
    372 }
    373 
    374 
    375 #undef __
    376 
    377 
    378 void ElementHandlerCompiler::CompileElementHandlers(
    379     MapHandleList* receiver_maps, CodeHandleList* handlers) {
    380   for (int i = 0; i < receiver_maps->length(); ++i) {
    381     Handle<Map> receiver_map = receiver_maps->at(i);
    382     Handle<Code> cached_stub;
    383 
    384     if ((receiver_map->instance_type() & kNotStringTag) == 0) {
    385       cached_stub = isolate()->builtins()->KeyedLoadIC_String();
    386     } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
    387       cached_stub = isolate()->builtins()->KeyedLoadIC_Slow();
    388     } else {
    389       bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
    390       ElementsKind elements_kind = receiver_map->elements_kind();
    391       if (receiver_map->has_indexed_interceptor()) {
    392         cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode();
    393       } else if (IsSloppyArgumentsElements(elements_kind)) {
    394         cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode();
    395       } else if (IsFastElementsKind(elements_kind) ||
    396                  IsExternalArrayElementsKind(elements_kind) ||
    397                  IsFixedTypedArrayElementsKind(elements_kind)) {
    398         cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind)
    399                           .GetCode();
    400       } else {
    401         DCHECK(elements_kind == DICTIONARY_ELEMENTS);
    402         cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
    403       }
    404     }
    405 
    406     handlers->Add(cached_stub);
    407   }
    408 }
    409 }
    410 }  // namespace v8::internal
    411