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