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