Home | History | Annotate | Download | only in src
      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 #include "src/ast.h"
      8 #include "src/code-stubs.h"
      9 #include "src/compiler.h"
     10 #include "src/ic/ic.h"
     11 #include "src/ic/stub-cache.h"
     12 #include "src/macro-assembler.h"
     13 #include "src/type-info.h"
     14 
     15 #include "src/objects-inl.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 
     20 
     21 TypeFeedbackOracle::TypeFeedbackOracle(
     22     Handle<Code> code, Handle<TypeFeedbackVector> feedback_vector,
     23     Handle<Context> native_context, Zone* zone)
     24     : native_context_(native_context), zone_(zone) {
     25   BuildDictionary(code);
     26   DCHECK(dictionary_->IsDictionary());
     27   // We make a copy of the feedback vector because a GC could clear
     28   // the type feedback info contained therein.
     29   // TODO(mvstanton): revisit the decision to copy when we weakly
     30   // traverse the feedback vector at GC time.
     31   feedback_vector_ = TypeFeedbackVector::Copy(isolate(), feedback_vector);
     32 }
     33 
     34 
     35 static uint32_t IdToKey(TypeFeedbackId ast_id) {
     36   return static_cast<uint32_t>(ast_id.ToInt());
     37 }
     38 
     39 
     40 Handle<Object> TypeFeedbackOracle::GetInfo(TypeFeedbackId ast_id) {
     41   int entry = dictionary_->FindEntry(IdToKey(ast_id));
     42   if (entry != UnseededNumberDictionary::kNotFound) {
     43     Object* value = dictionary_->ValueAt(entry);
     44     if (value->IsCell()) {
     45       Cell* cell = Cell::cast(value);
     46       return Handle<Object>(cell->value(), isolate());
     47     } else {
     48       return Handle<Object>(value, isolate());
     49     }
     50   }
     51   return Handle<Object>::cast(isolate()->factory()->undefined_value());
     52 }
     53 
     54 
     55 Handle<Object> TypeFeedbackOracle::GetInfo(int slot) {
     56   DCHECK(slot >= 0 && slot < feedback_vector_->length());
     57   Object* obj = feedback_vector_->get(slot);
     58   if (!obj->IsJSFunction() ||
     59       !CanRetainOtherContext(JSFunction::cast(obj), *native_context_)) {
     60     return Handle<Object>(obj, isolate());
     61   }
     62   return Handle<Object>::cast(isolate()->factory()->undefined_value());
     63 }
     64 
     65 
     66 bool TypeFeedbackOracle::LoadIsUninitialized(TypeFeedbackId id) {
     67   Handle<Object> maybe_code = GetInfo(id);
     68   if (maybe_code->IsCode()) {
     69     Handle<Code> code = Handle<Code>::cast(maybe_code);
     70     return code->is_inline_cache_stub() && code->ic_state() == UNINITIALIZED;
     71   }
     72   return false;
     73 }
     74 
     75 
     76 bool TypeFeedbackOracle::StoreIsUninitialized(TypeFeedbackId ast_id) {
     77   Handle<Object> maybe_code = GetInfo(ast_id);
     78   if (!maybe_code->IsCode()) return false;
     79   Handle<Code> code = Handle<Code>::cast(maybe_code);
     80   return code->ic_state() == UNINITIALIZED;
     81 }
     82 
     83 
     84 bool TypeFeedbackOracle::StoreIsKeyedPolymorphic(TypeFeedbackId ast_id) {
     85   Handle<Object> maybe_code = GetInfo(ast_id);
     86   if (maybe_code->IsCode()) {
     87     Handle<Code> code = Handle<Code>::cast(maybe_code);
     88     return code->is_keyed_store_stub() &&
     89         code->ic_state() == POLYMORPHIC;
     90   }
     91   return false;
     92 }
     93 
     94 
     95 bool TypeFeedbackOracle::CallIsMonomorphic(int slot) {
     96   Handle<Object> value = GetInfo(slot);
     97   return value->IsAllocationSite() || value->IsJSFunction();
     98 }
     99 
    100 
    101 bool TypeFeedbackOracle::CallNewIsMonomorphic(int slot) {
    102   Handle<Object> info = GetInfo(slot);
    103   return FLAG_pretenuring_call_new
    104       ? info->IsJSFunction()
    105       : info->IsAllocationSite() || info->IsJSFunction();
    106 }
    107 
    108 
    109 byte TypeFeedbackOracle::ForInType(int feedback_vector_slot) {
    110   Handle<Object> value = GetInfo(feedback_vector_slot);
    111   return value.is_identical_to(
    112              TypeFeedbackVector::UninitializedSentinel(isolate()))
    113              ? ForInStatement::FAST_FOR_IN
    114              : ForInStatement::SLOW_FOR_IN;
    115 }
    116 
    117 
    118 KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
    119     TypeFeedbackId ast_id) {
    120   Handle<Object> maybe_code = GetInfo(ast_id);
    121   if (maybe_code->IsCode()) {
    122     Handle<Code> code = Handle<Code>::cast(maybe_code);
    123     if (code->kind() == Code::KEYED_STORE_IC) {
    124       return KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state());
    125     }
    126   }
    127   return STANDARD_STORE;
    128 }
    129 
    130 
    131 Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(int slot) {
    132   Handle<Object> info = GetInfo(slot);
    133   if (info->IsAllocationSite()) {
    134     return Handle<JSFunction>(isolate()->native_context()->array_function());
    135   }
    136 
    137   return Handle<JSFunction>::cast(info);
    138 }
    139 
    140 
    141 Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(int slot) {
    142   Handle<Object> info = GetInfo(slot);
    143   if (FLAG_pretenuring_call_new || info->IsJSFunction()) {
    144     return Handle<JSFunction>::cast(info);
    145   }
    146 
    147   DCHECK(info->IsAllocationSite());
    148   return Handle<JSFunction>(isolate()->native_context()->array_function());
    149 }
    150 
    151 
    152 Handle<AllocationSite> TypeFeedbackOracle::GetCallAllocationSite(int slot) {
    153   Handle<Object> info = GetInfo(slot);
    154   if (info->IsAllocationSite()) {
    155     return Handle<AllocationSite>::cast(info);
    156   }
    157   return Handle<AllocationSite>::null();
    158 }
    159 
    160 
    161 Handle<AllocationSite> TypeFeedbackOracle::GetCallNewAllocationSite(int slot) {
    162   Handle<Object> info = GetInfo(slot);
    163   if (FLAG_pretenuring_call_new || info->IsAllocationSite()) {
    164     return Handle<AllocationSite>::cast(info);
    165   }
    166   return Handle<AllocationSite>::null();
    167 }
    168 
    169 
    170 bool TypeFeedbackOracle::LoadIsBuiltin(
    171     TypeFeedbackId id, Builtins::Name builtin) {
    172   return *GetInfo(id) == isolate()->builtins()->builtin(builtin);
    173 }
    174 
    175 
    176 void TypeFeedbackOracle::CompareType(TypeFeedbackId id,
    177                                      Type** left_type,
    178                                      Type** right_type,
    179                                      Type** combined_type) {
    180   Handle<Object> info = GetInfo(id);
    181   if (!info->IsCode()) {
    182     // For some comparisons we don't have ICs, e.g. LiteralCompareTypeof.
    183     *left_type = *right_type = *combined_type = Type::None(zone());
    184     return;
    185   }
    186   Handle<Code> code = Handle<Code>::cast(info);
    187 
    188   Handle<Map> map;
    189   Map* raw_map = code->FindFirstMap();
    190   if (raw_map != NULL) {
    191     if (Map::TryUpdate(handle(raw_map)).ToHandle(&map) &&
    192         CanRetainOtherContext(*map, *native_context_)) {
    193       map = Handle<Map>::null();
    194     }
    195   }
    196 
    197   if (code->is_compare_ic_stub()) {
    198     CompareICStub stub(code->stub_key(), isolate());
    199     *left_type = CompareICState::StateToType(zone(), stub.left());
    200     *right_type = CompareICState::StateToType(zone(), stub.right());
    201     *combined_type = CompareICState::StateToType(zone(), stub.state(), map);
    202   } else if (code->is_compare_nil_ic_stub()) {
    203     CompareNilICStub stub(isolate(), code->extra_ic_state());
    204     *combined_type = stub.GetType(zone(), map);
    205     *left_type = *right_type = stub.GetInputType(zone(), map);
    206   }
    207 }
    208 
    209 
    210 void TypeFeedbackOracle::BinaryType(TypeFeedbackId id,
    211                                     Type** left,
    212                                     Type** right,
    213                                     Type** result,
    214                                     Maybe<int>* fixed_right_arg,
    215                                     Handle<AllocationSite>* allocation_site,
    216                                     Token::Value op) {
    217   Handle<Object> object = GetInfo(id);
    218   if (!object->IsCode()) {
    219     // For some binary ops we don't have ICs, e.g. Token::COMMA, but for the
    220     // operations covered by the BinaryOpIC we should always have them.
    221     DCHECK(op < BinaryOpICState::FIRST_TOKEN ||
    222            op > BinaryOpICState::LAST_TOKEN);
    223     *left = *right = *result = Type::None(zone());
    224     *fixed_right_arg = Maybe<int>();
    225     *allocation_site = Handle<AllocationSite>::null();
    226     return;
    227   }
    228   Handle<Code> code = Handle<Code>::cast(object);
    229   DCHECK_EQ(Code::BINARY_OP_IC, code->kind());
    230   BinaryOpICState state(isolate(), code->extra_ic_state());
    231   DCHECK_EQ(op, state.op());
    232 
    233   *left = state.GetLeftType(zone());
    234   *right = state.GetRightType(zone());
    235   *result = state.GetResultType(zone());
    236   *fixed_right_arg = state.fixed_right_arg();
    237 
    238   AllocationSite* first_allocation_site = code->FindFirstAllocationSite();
    239   if (first_allocation_site != NULL) {
    240     *allocation_site = handle(first_allocation_site);
    241   } else {
    242     *allocation_site = Handle<AllocationSite>::null();
    243   }
    244 }
    245 
    246 
    247 Type* TypeFeedbackOracle::CountType(TypeFeedbackId id) {
    248   Handle<Object> object = GetInfo(id);
    249   if (!object->IsCode()) return Type::None(zone());
    250   Handle<Code> code = Handle<Code>::cast(object);
    251   DCHECK_EQ(Code::BINARY_OP_IC, code->kind());
    252   BinaryOpICState state(isolate(), code->extra_ic_state());
    253   return state.GetLeftType(zone());
    254 }
    255 
    256 
    257 void TypeFeedbackOracle::PropertyReceiverTypes(TypeFeedbackId id,
    258                                                Handle<String> name,
    259                                                SmallMapList* receiver_types) {
    260   receiver_types->Clear();
    261   Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
    262   CollectReceiverTypes(id, name, flags, receiver_types);
    263 }
    264 
    265 
    266 void TypeFeedbackOracle::KeyedPropertyReceiverTypes(
    267     TypeFeedbackId id, SmallMapList* receiver_types, bool* is_string) {
    268   receiver_types->Clear();
    269   *is_string = false;
    270   if (LoadIsBuiltin(id, Builtins::kKeyedLoadIC_String)) {
    271     *is_string = true;
    272   } else {
    273     CollectReceiverTypes(id, receiver_types);
    274   }
    275 }
    276 
    277 
    278 void TypeFeedbackOracle::AssignmentReceiverTypes(
    279     TypeFeedbackId id, Handle<String> name, SmallMapList* receiver_types) {
    280   receiver_types->Clear();
    281   Code::Flags flags = Code::ComputeHandlerFlags(Code::STORE_IC);
    282   CollectReceiverTypes(id, name, flags, receiver_types);
    283 }
    284 
    285 
    286 void TypeFeedbackOracle::KeyedAssignmentReceiverTypes(
    287     TypeFeedbackId id, SmallMapList* receiver_types,
    288     KeyedAccessStoreMode* store_mode) {
    289   receiver_types->Clear();
    290   CollectReceiverTypes(id, receiver_types);
    291   *store_mode = GetStoreMode(id);
    292 }
    293 
    294 
    295 void TypeFeedbackOracle::CountReceiverTypes(TypeFeedbackId id,
    296                                             SmallMapList* receiver_types) {
    297   receiver_types->Clear();
    298   CollectReceiverTypes(id, receiver_types);
    299 }
    300 
    301 
    302 void TypeFeedbackOracle::CollectReceiverTypes(TypeFeedbackId ast_id,
    303                                               Handle<String> name,
    304                                               Code::Flags flags,
    305                                               SmallMapList* types) {
    306   Handle<Object> object = GetInfo(ast_id);
    307   if (object->IsUndefined() || object->IsSmi()) return;
    308 
    309   DCHECK(object->IsCode());
    310   Handle<Code> code(Handle<Code>::cast(object));
    311 
    312   if (FLAG_collect_megamorphic_maps_from_stub_cache &&
    313       code->ic_state() == MEGAMORPHIC) {
    314     types->Reserve(4, zone());
    315     isolate()->stub_cache()->CollectMatchingMaps(
    316         types, name, flags, native_context_, zone());
    317   } else {
    318     CollectReceiverTypes(ast_id, types);
    319   }
    320 }
    321 
    322 
    323 // Check if a map originates from a given native context. We use this
    324 // information to filter out maps from different context to avoid
    325 // retaining objects from different tabs in Chrome via optimized code.
    326 bool TypeFeedbackOracle::CanRetainOtherContext(Map* map,
    327                                                Context* native_context) {
    328   Object* constructor = NULL;
    329   while (!map->prototype()->IsNull()) {
    330     constructor = map->constructor();
    331     if (!constructor->IsNull()) {
    332       // If the constructor is not null or a JSFunction, we have to
    333       // conservatively assume that it may retain a native context.
    334       if (!constructor->IsJSFunction()) return true;
    335       // Check if the constructor directly references a foreign context.
    336       if (CanRetainOtherContext(JSFunction::cast(constructor),
    337                                 native_context)) {
    338         return true;
    339       }
    340     }
    341     map = HeapObject::cast(map->prototype())->map();
    342   }
    343   constructor = map->constructor();
    344   if (constructor->IsNull()) return false;
    345   JSFunction* function = JSFunction::cast(constructor);
    346   return CanRetainOtherContext(function, native_context);
    347 }
    348 
    349 
    350 bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function,
    351                                                Context* native_context) {
    352   return function->context()->global_object() != native_context->global_object()
    353       && function->context()->global_object() != native_context->builtins();
    354 }
    355 
    356 
    357 void TypeFeedbackOracle::CollectReceiverTypes(TypeFeedbackId ast_id,
    358                                               SmallMapList* types) {
    359   Handle<Object> object = GetInfo(ast_id);
    360   if (!object->IsCode()) return;
    361   Handle<Code> code = Handle<Code>::cast(object);
    362   MapHandleList maps;
    363   if (code->ic_state() == MONOMORPHIC) {
    364     Map* map = code->FindFirstMap();
    365     if (map != NULL) maps.Add(handle(map));
    366   } else if (code->ic_state() == POLYMORPHIC) {
    367     code->FindAllMaps(&maps);
    368   } else {
    369     return;
    370   }
    371   types->Reserve(maps.length(), zone());
    372   for (int i = 0; i < maps.length(); i++) {
    373     Handle<Map> map(maps.at(i));
    374     if (!CanRetainOtherContext(*map, *native_context_)) {
    375       types->AddMapIfMissing(map, zone());
    376     }
    377   }
    378 }
    379 
    380 
    381 byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
    382   Handle<Object> object = GetInfo(id);
    383   return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
    384 }
    385 
    386 
    387 // Things are a bit tricky here: The iterator for the RelocInfos and the infos
    388 // themselves are not GC-safe, so we first get all infos, then we create the
    389 // dictionary (possibly triggering GC), and finally we relocate the collected
    390 // infos before we process them.
    391 void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) {
    392   DisallowHeapAllocation no_allocation;
    393   ZoneList<RelocInfo> infos(16, zone());
    394   HandleScope scope(isolate());
    395   GetRelocInfos(code, &infos);
    396   CreateDictionary(code, &infos);
    397   ProcessRelocInfos(&infos);
    398   // Allocate handle in the parent scope.
    399   dictionary_ = scope.CloseAndEscape(dictionary_);
    400 }
    401 
    402 
    403 void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code,
    404                                        ZoneList<RelocInfo>* infos) {
    405   int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID);
    406   for (RelocIterator it(*code, mask); !it.done(); it.next()) {
    407     infos->Add(*it.rinfo(), zone());
    408   }
    409 }
    410 
    411 
    412 void TypeFeedbackOracle::CreateDictionary(Handle<Code> code,
    413                                           ZoneList<RelocInfo>* infos) {
    414   AllowHeapAllocation allocation_allowed;
    415   Code* old_code = *code;
    416   dictionary_ = UnseededNumberDictionary::New(isolate(), infos->length());
    417   RelocateRelocInfos(infos, old_code, *code);
    418 }
    419 
    420 
    421 void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos,
    422                                             Code* old_code,
    423                                             Code* new_code) {
    424   for (int i = 0; i < infos->length(); i++) {
    425     RelocInfo* info = &(*infos)[i];
    426     info->set_host(new_code);
    427     info->set_pc(new_code->instruction_start() +
    428                  (info->pc() - old_code->instruction_start()));
    429   }
    430 }
    431 
    432 
    433 void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
    434   for (int i = 0; i < infos->length(); i++) {
    435     RelocInfo reloc_entry = (*infos)[i];
    436     Address target_address = reloc_entry.target_address();
    437     TypeFeedbackId ast_id =
    438         TypeFeedbackId(static_cast<unsigned>((*infos)[i].data()));
    439     Code* target = Code::GetCodeFromTargetAddress(target_address);
    440     switch (target->kind()) {
    441       case Code::LOAD_IC:
    442       case Code::STORE_IC:
    443       case Code::KEYED_LOAD_IC:
    444       case Code::KEYED_STORE_IC:
    445       case Code::BINARY_OP_IC:
    446       case Code::COMPARE_IC:
    447       case Code::TO_BOOLEAN_IC:
    448       case Code::COMPARE_NIL_IC:
    449         SetInfo(ast_id, target);
    450         break;
    451 
    452       default:
    453         break;
    454     }
    455   }
    456 }
    457 
    458 
    459 void TypeFeedbackOracle::SetInfo(TypeFeedbackId ast_id, Object* target) {
    460   DCHECK(dictionary_->FindEntry(IdToKey(ast_id)) ==
    461          UnseededNumberDictionary::kNotFound);
    462   // Dictionary has been allocated with sufficient size for all elements.
    463   DisallowHeapAllocation no_need_to_resize_dictionary;
    464   HandleScope scope(isolate());
    465   USE(UnseededNumberDictionary::AtNumberPut(
    466       dictionary_, IdToKey(ast_id), handle(target, isolate())));
    467 }
    468 
    469 
    470 } }  // namespace v8::internal
    471