| OLD | NEW |
| (Empty) |
| 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/api.h" | |
| 8 #include "src/arguments.h" | |
| 9 #include "src/ast.h" | |
| 10 #include "src/code-stubs.h" | |
| 11 #include "src/cpu-profiler.h" | |
| 12 #include "src/gdb-jit.h" | |
| 13 #include "src/ic-inl.h" | |
| 14 #include "src/stub-cache.h" | |
| 15 #include "src/type-info.h" | |
| 16 #include "src/vm-state-inl.h" | |
| 17 | |
| 18 namespace v8 { | |
| 19 namespace internal { | |
| 20 | |
| 21 // ----------------------------------------------------------------------- | |
| 22 // StubCache implementation. | |
| 23 | |
| 24 | |
| 25 StubCache::StubCache(Isolate* isolate) | |
| 26 : isolate_(isolate) { } | |
| 27 | |
| 28 | |
| 29 void StubCache::Initialize() { | |
| 30 DCHECK(IsPowerOf2(kPrimaryTableSize)); | |
| 31 DCHECK(IsPowerOf2(kSecondaryTableSize)); | |
| 32 Clear(); | |
| 33 } | |
| 34 | |
| 35 | |
| 36 static Code::Flags CommonStubCacheChecks(Name* name, Map* map, | |
| 37 Code::Flags flags) { | |
| 38 flags = Code::RemoveTypeAndHolderFromFlags(flags); | |
| 39 | |
| 40 // Validate that the name does not move on scavenge, and that we | |
| 41 // can use identity checks instead of structural equality checks. | |
| 42 DCHECK(!name->GetHeap()->InNewSpace(name)); | |
| 43 DCHECK(name->IsUniqueName()); | |
| 44 | |
| 45 // The state bits are not important to the hash function because the stub | |
| 46 // cache only contains handlers. Make sure that the bits are the least | |
| 47 // significant so they will be the ones masked out. | |
| 48 DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(flags)); | |
| 49 STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); | |
| 50 | |
| 51 // Make sure that the code type and cache holder are not included in the hash. | |
| 52 DCHECK(Code::ExtractTypeFromFlags(flags) == 0); | |
| 53 DCHECK(Code::ExtractCacheHolderFromFlags(flags) == 0); | |
| 54 | |
| 55 return flags; | |
| 56 } | |
| 57 | |
| 58 | |
| 59 Code* StubCache::Set(Name* name, Map* map, Code* code) { | |
| 60 Code::Flags flags = CommonStubCacheChecks(name, map, code->flags()); | |
| 61 | |
| 62 // Compute the primary entry. | |
| 63 int primary_offset = PrimaryOffset(name, flags, map); | |
| 64 Entry* primary = entry(primary_, primary_offset); | |
| 65 Code* old_code = primary->value; | |
| 66 | |
| 67 // If the primary entry has useful data in it, we retire it to the | |
| 68 // secondary cache before overwriting it. | |
| 69 if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) { | |
| 70 Map* old_map = primary->map; | |
| 71 Code::Flags old_flags = | |
| 72 Code::RemoveTypeAndHolderFromFlags(old_code->flags()); | |
| 73 int seed = PrimaryOffset(primary->key, old_flags, old_map); | |
| 74 int secondary_offset = SecondaryOffset(primary->key, old_flags, seed); | |
| 75 Entry* secondary = entry(secondary_, secondary_offset); | |
| 76 *secondary = *primary; | |
| 77 } | |
| 78 | |
| 79 // Update primary cache. | |
| 80 primary->key = name; | |
| 81 primary->value = code; | |
| 82 primary->map = map; | |
| 83 isolate()->counters()->megamorphic_stub_cache_updates()->Increment(); | |
| 84 return code; | |
| 85 } | |
| 86 | |
| 87 | |
| 88 Code* StubCache::Get(Name* name, Map* map, Code::Flags flags) { | |
| 89 flags = CommonStubCacheChecks(name, map, flags); | |
| 90 int primary_offset = PrimaryOffset(name, flags, map); | |
| 91 Entry* primary = entry(primary_, primary_offset); | |
| 92 if (primary->key == name && primary->map == map) { | |
| 93 return primary->value; | |
| 94 } | |
| 95 int secondary_offset = SecondaryOffset(name, flags, primary_offset); | |
| 96 Entry* secondary = entry(secondary_, secondary_offset); | |
| 97 if (secondary->key == name && secondary->map == map) { | |
| 98 return secondary->value; | |
| 99 } | |
| 100 return NULL; | |
| 101 } | |
| 102 | |
| 103 | |
| 104 Handle<Code> PropertyICCompiler::Find(Handle<Name> name, | |
| 105 Handle<Map> stub_holder, Code::Kind kind, | |
| 106 ExtraICState extra_state, | |
| 107 CacheHolderFlag cache_holder) { | |
| 108 Code::Flags flags = Code::ComputeMonomorphicFlags( | |
| 109 kind, extra_state, cache_holder); | |
| 110 Object* probe = stub_holder->FindInCodeCache(*name, flags); | |
| 111 if (probe->IsCode()) return handle(Code::cast(probe)); | |
| 112 return Handle<Code>::null(); | |
| 113 } | |
| 114 | |
| 115 | |
| 116 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name, | |
| 117 Handle<Map> stub_holder, | |
| 118 Code::Kind kind, | |
| 119 CacheHolderFlag cache_holder, | |
| 120 Code::StubType type) { | |
| 121 Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder); | |
| 122 Object* probe = stub_holder->FindInCodeCache(*name, flags); | |
| 123 if (probe->IsCode()) return handle(Code::cast(probe)); | |
| 124 return Handle<Code>::null(); | |
| 125 } | |
| 126 | |
| 127 | |
| 128 Handle<Code> PropertyICCompiler::ComputeMonomorphic( | |
| 129 Code::Kind kind, Handle<Name> name, Handle<HeapType> type, | |
| 130 Handle<Code> handler, ExtraICState extra_ic_state) { | |
| 131 Isolate* isolate = name->GetIsolate(); | |
| 132 if (handler.is_identical_to(isolate->builtins()->LoadIC_Normal()) || | |
| 133 handler.is_identical_to(isolate->builtins()->StoreIC_Normal())) { | |
| 134 name = isolate->factory()->normal_ic_symbol(); | |
| 135 } | |
| 136 | |
| 137 CacheHolderFlag flag; | |
| 138 Handle<Map> stub_holder = IC::GetICCacheHolder(*type, isolate, &flag); | |
| 139 | |
| 140 Handle<Code> ic; | |
| 141 // There are multiple string maps that all use the same prototype. That | |
| 142 // prototype cannot hold multiple handlers, one for each of the string maps, | |
| 143 // for a single name. Hence, turn off caching of the IC. | |
| 144 bool can_be_cached = !type->Is(HeapType::String()); | |
| 145 if (can_be_cached) { | |
| 146 ic = Find(name, stub_holder, kind, extra_ic_state, flag); | |
| 147 if (!ic.is_null()) return ic; | |
| 148 } | |
| 149 | |
| 150 #ifdef DEBUG | |
| 151 if (kind == Code::KEYED_STORE_IC) { | |
| 152 DCHECK(STANDARD_STORE == | |
| 153 KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); | |
| 154 } | |
| 155 #endif | |
| 156 | |
| 157 PropertyICCompiler ic_compiler(isolate, kind, extra_ic_state, flag); | |
| 158 ic = ic_compiler.CompileMonomorphic(type, handler, name, PROPERTY); | |
| 159 | |
| 160 if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); | |
| 161 return ic; | |
| 162 } | |
| 163 | |
| 164 | |
| 165 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent( | |
| 166 Handle<Name> name, Handle<HeapType> type) { | |
| 167 Isolate* isolate = name->GetIsolate(); | |
| 168 Handle<Map> receiver_map = IC::TypeToMap(*type, isolate); | |
| 169 if (receiver_map->prototype()->IsNull()) { | |
| 170 // TODO(jkummerow/verwaest): If there is no prototype and the property | |
| 171 // is nonexistent, introduce a builtin to handle this (fast properties | |
| 172 // -> return undefined, dictionary properties -> do negative lookup). | |
| 173 return Handle<Code>(); | |
| 174 } | |
| 175 CacheHolderFlag flag; | |
| 176 Handle<Map> stub_holder_map = | |
| 177 IC::GetHandlerCacheHolder(*type, false, isolate, &flag); | |
| 178 | |
| 179 // If no dictionary mode objects are present in the prototype chain, the load | |
| 180 // nonexistent IC stub can be shared for all names for a given map and we use | |
| 181 // the empty string for the map cache in that case. If there are dictionary | |
| 182 // mode objects involved, we need to do negative lookups in the stub and | |
| 183 // therefore the stub will be specific to the name. | |
| 184 Handle<Name> cache_name = | |
| 185 receiver_map->is_dictionary_map() | |
| 186 ? name | |
| 187 : Handle<Name>::cast(isolate->factory()->nonexistent_symbol()); | |
| 188 Handle<Map> current_map = stub_holder_map; | |
| 189 Handle<JSObject> last(JSObject::cast(receiver_map->prototype())); | |
| 190 while (true) { | |
| 191 if (current_map->is_dictionary_map()) cache_name = name; | |
| 192 if (current_map->prototype()->IsNull()) break; | |
| 193 last = handle(JSObject::cast(current_map->prototype())); | |
| 194 current_map = handle(last->map()); | |
| 195 } | |
| 196 // Compile the stub that is either shared for all names or | |
| 197 // name specific if there are global objects involved. | |
| 198 Handle<Code> handler = PropertyHandlerCompiler::Find( | |
| 199 cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST); | |
| 200 if (!handler.is_null()) return handler; | |
| 201 | |
| 202 NamedLoadHandlerCompiler compiler(isolate, type, last, flag); | |
| 203 handler = compiler.CompileLoadNonexistent(cache_name); | |
| 204 Map::UpdateCodeCache(stub_holder_map, cache_name, handler); | |
| 205 return handler; | |
| 206 } | |
| 207 | |
| 208 | |
| 209 Handle<Code> PropertyICCompiler::ComputeKeyedLoadMonomorphic( | |
| 210 Handle<Map> receiver_map) { | |
| 211 Isolate* isolate = receiver_map->GetIsolate(); | |
| 212 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); | |
| 213 Handle<Name> name = isolate->factory()->KeyedLoadMonomorphic_string(); | |
| 214 | |
| 215 Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate); | |
| 216 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
| 217 | |
| 218 ElementsKind elements_kind = receiver_map->elements_kind(); | |
| 219 Handle<Code> stub; | |
| 220 if (receiver_map->has_fast_elements() || | |
| 221 receiver_map->has_external_array_elements() || | |
| 222 receiver_map->has_fixed_typed_array_elements()) { | |
| 223 stub = LoadFastElementStub(isolate, | |
| 224 receiver_map->instance_type() == JS_ARRAY_TYPE, | |
| 225 elements_kind).GetCode(); | |
| 226 } else { | |
| 227 stub = FLAG_compiled_keyed_dictionary_loads | |
| 228 ? LoadDictionaryElementStub(isolate).GetCode() | |
| 229 : LoadDictionaryElementPlatformStub(isolate).GetCode(); | |
| 230 } | |
| 231 PropertyICCompiler compiler(isolate, Code::KEYED_LOAD_IC); | |
| 232 Handle<Code> code = | |
| 233 compiler.CompileMonomorphic(HeapType::Class(receiver_map, isolate), stub, | |
| 234 isolate->factory()->empty_string(), ELEMENT); | |
| 235 | |
| 236 Map::UpdateCodeCache(receiver_map, name, code); | |
| 237 return code; | |
| 238 } | |
| 239 | |
| 240 | |
| 241 Handle<Code> PropertyICCompiler::ComputeKeyedStoreMonomorphic( | |
| 242 Handle<Map> receiver_map, StrictMode strict_mode, | |
| 243 KeyedAccessStoreMode store_mode) { | |
| 244 Isolate* isolate = receiver_map->GetIsolate(); | |
| 245 ExtraICState extra_state = | |
| 246 KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); | |
| 247 Code::Flags flags = | |
| 248 Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, extra_state); | |
| 249 | |
| 250 DCHECK(store_mode == STANDARD_STORE || | |
| 251 store_mode == STORE_AND_GROW_NO_TRANSITION || | |
| 252 store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || | |
| 253 store_mode == STORE_NO_TRANSITION_HANDLE_COW); | |
| 254 | |
| 255 Handle<String> name = isolate->factory()->KeyedStoreMonomorphic_string(); | |
| 256 Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate); | |
| 257 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
| 258 | |
| 259 PropertyICCompiler compiler(isolate, Code::KEYED_STORE_IC, extra_state); | |
| 260 Handle<Code> code = | |
| 261 compiler.CompileKeyedStoreMonomorphic(receiver_map, store_mode); | |
| 262 | |
| 263 Map::UpdateCodeCache(receiver_map, name, code); | |
| 264 DCHECK(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) | |
| 265 == store_mode); | |
| 266 return code; | |
| 267 } | |
| 268 | |
| 269 | |
| 270 #define CALL_LOGGER_TAG(kind, type) (Logger::KEYED_##type) | |
| 271 | |
| 272 static void FillCache(Isolate* isolate, Handle<Code> code) { | |
| 273 Handle<UnseededNumberDictionary> dictionary = | |
| 274 UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), | |
| 275 code->flags(), | |
| 276 code); | |
| 277 isolate->heap()->public_set_non_monomorphic_cache(*dictionary); | |
| 278 } | |
| 279 | |
| 280 | |
| 281 Code* PropertyICCompiler::FindPreMonomorphic(Isolate* isolate, Code::Kind kind, | |
| 282 ExtraICState state) { | |
| 283 Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, state); | |
| 284 UnseededNumberDictionary* dictionary = | |
| 285 isolate->heap()->non_monomorphic_cache(); | |
| 286 int entry = dictionary->FindEntry(isolate, flags); | |
| 287 DCHECK(entry != -1); | |
| 288 Object* code = dictionary->ValueAt(entry); | |
| 289 // This might be called during the marking phase of the collector | |
| 290 // hence the unchecked cast. | |
| 291 return reinterpret_cast<Code*>(code); | |
| 292 } | |
| 293 | |
| 294 | |
| 295 Handle<Code> PropertyICCompiler::ComputeLoad(Isolate* isolate, | |
| 296 InlineCacheState ic_state, | |
| 297 ExtraICState extra_state) { | |
| 298 Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, ic_state, extra_state); | |
| 299 Handle<UnseededNumberDictionary> cache = | |
| 300 isolate->factory()->non_monomorphic_cache(); | |
| 301 int entry = cache->FindEntry(isolate, flags); | |
| 302 if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); | |
| 303 | |
| 304 PropertyICCompiler compiler(isolate, Code::LOAD_IC); | |
| 305 Handle<Code> code; | |
| 306 if (ic_state == UNINITIALIZED) { | |
| 307 code = compiler.CompileLoadInitialize(flags); | |
| 308 } else if (ic_state == PREMONOMORPHIC) { | |
| 309 code = compiler.CompileLoadPreMonomorphic(flags); | |
| 310 } else if (ic_state == MEGAMORPHIC) { | |
| 311 code = compiler.CompileLoadMegamorphic(flags); | |
| 312 } else { | |
| 313 UNREACHABLE(); | |
| 314 } | |
| 315 FillCache(isolate, code); | |
| 316 return code; | |
| 317 } | |
| 318 | |
| 319 | |
| 320 Handle<Code> PropertyICCompiler::ComputeStore(Isolate* isolate, | |
| 321 InlineCacheState ic_state, | |
| 322 ExtraICState extra_state) { | |
| 323 Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, ic_state, extra_state); | |
| 324 Handle<UnseededNumberDictionary> cache = | |
| 325 isolate->factory()->non_monomorphic_cache(); | |
| 326 int entry = cache->FindEntry(isolate, flags); | |
| 327 if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); | |
| 328 | |
| 329 PropertyICCompiler compiler(isolate, Code::STORE_IC); | |
| 330 Handle<Code> code; | |
| 331 if (ic_state == UNINITIALIZED) { | |
| 332 code = compiler.CompileStoreInitialize(flags); | |
| 333 } else if (ic_state == PREMONOMORPHIC) { | |
| 334 code = compiler.CompileStorePreMonomorphic(flags); | |
| 335 } else if (ic_state == GENERIC) { | |
| 336 code = compiler.CompileStoreGeneric(flags); | |
| 337 } else if (ic_state == MEGAMORPHIC) { | |
| 338 code = compiler.CompileStoreMegamorphic(flags); | |
| 339 } else { | |
| 340 UNREACHABLE(); | |
| 341 } | |
| 342 | |
| 343 FillCache(isolate, code); | |
| 344 return code; | |
| 345 } | |
| 346 | |
| 347 | |
| 348 Handle<Code> PropertyICCompiler::ComputeCompareNil(Handle<Map> receiver_map, | |
| 349 CompareNilICStub* stub) { | |
| 350 Isolate* isolate = receiver_map->GetIsolate(); | |
| 351 Handle<String> name(isolate->heap()->empty_string()); | |
| 352 if (!receiver_map->is_dictionary_map()) { | |
| 353 Handle<Code> cached_ic = | |
| 354 Find(name, receiver_map, Code::COMPARE_NIL_IC, stub->GetExtraICState()); | |
| 355 if (!cached_ic.is_null()) return cached_ic; | |
| 356 } | |
| 357 | |
| 358 Code::FindAndReplacePattern pattern; | |
| 359 pattern.Add(isolate->factory()->meta_map(), receiver_map); | |
| 360 Handle<Code> ic = stub->GetCodeCopy(pattern); | |
| 361 | |
| 362 if (!receiver_map->is_dictionary_map()) { | |
| 363 Map::UpdateCodeCache(receiver_map, name, ic); | |
| 364 } | |
| 365 | |
| 366 return ic; | |
| 367 } | |
| 368 | |
| 369 | |
| 370 // TODO(verwaest): Change this method so it takes in a TypeHandleList. | |
| 371 Handle<Code> PropertyICCompiler::ComputeKeyedLoadPolymorphic( | |
| 372 MapHandleList* receiver_maps) { | |
| 373 Isolate* isolate = receiver_maps->at(0)->GetIsolate(); | |
| 374 Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); | |
| 375 Handle<PolymorphicCodeCache> cache = | |
| 376 isolate->factory()->polymorphic_code_cache(); | |
| 377 Handle<Object> probe = cache->Lookup(receiver_maps, flags); | |
| 378 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
| 379 | |
| 380 TypeHandleList types(receiver_maps->length()); | |
| 381 for (int i = 0; i < receiver_maps->length(); i++) { | |
| 382 types.Add(HeapType::Class(receiver_maps->at(i), isolate)); | |
| 383 } | |
| 384 CodeHandleList handlers(receiver_maps->length()); | |
| 385 ElementHandlerCompiler compiler(isolate); | |
| 386 compiler.CompileElementHandlers(receiver_maps, &handlers); | |
| 387 PropertyICCompiler ic_compiler(isolate, Code::KEYED_LOAD_IC); | |
| 388 Handle<Code> code = ic_compiler.CompilePolymorphic( | |
| 389 &types, &handlers, isolate->factory()->empty_string(), Code::NORMAL, | |
| 390 ELEMENT); | |
| 391 | |
| 392 isolate->counters()->keyed_load_polymorphic_stubs()->Increment(); | |
| 393 | |
| 394 PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); | |
| 395 return code; | |
| 396 } | |
| 397 | |
| 398 | |
| 399 Handle<Code> PropertyICCompiler::ComputePolymorphic( | |
| 400 Code::Kind kind, TypeHandleList* types, CodeHandleList* handlers, | |
| 401 int valid_types, Handle<Name> name, ExtraICState extra_ic_state) { | |
| 402 Handle<Code> handler = handlers->at(0); | |
| 403 Code::StubType type = valid_types == 1 ? handler->type() : Code::NORMAL; | |
| 404 DCHECK(kind == Code::LOAD_IC || kind == Code::STORE_IC); | |
| 405 PropertyICCompiler ic_compiler(name->GetIsolate(), kind, extra_ic_state); | |
| 406 return ic_compiler.CompilePolymorphic(types, handlers, name, type, PROPERTY); | |
| 407 } | |
| 408 | |
| 409 | |
| 410 Handle<Code> PropertyICCompiler::ComputeKeyedStorePolymorphic( | |
| 411 MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, | |
| 412 StrictMode strict_mode) { | |
| 413 Isolate* isolate = receiver_maps->at(0)->GetIsolate(); | |
| 414 DCHECK(store_mode == STANDARD_STORE || | |
| 415 store_mode == STORE_AND_GROW_NO_TRANSITION || | |
| 416 store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || | |
| 417 store_mode == STORE_NO_TRANSITION_HANDLE_COW); | |
| 418 Handle<PolymorphicCodeCache> cache = | |
| 419 isolate->factory()->polymorphic_code_cache(); | |
| 420 ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( | |
| 421 strict_mode, store_mode); | |
| 422 Code::Flags flags = | |
| 423 Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); | |
| 424 Handle<Object> probe = cache->Lookup(receiver_maps, flags); | |
| 425 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
| 426 | |
| 427 PropertyICCompiler compiler(isolate, Code::KEYED_STORE_IC, extra_state); | |
| 428 Handle<Code> code = | |
| 429 compiler.CompileKeyedStorePolymorphic(receiver_maps, store_mode); | |
| 430 PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); | |
| 431 return code; | |
| 432 } | |
| 433 | |
| 434 | |
| 435 void StubCache::Clear() { | |
| 436 Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); | |
| 437 for (int i = 0; i < kPrimaryTableSize; i++) { | |
| 438 primary_[i].key = isolate()->heap()->empty_string(); | |
| 439 primary_[i].map = NULL; | |
| 440 primary_[i].value = empty; | |
| 441 } | |
| 442 for (int j = 0; j < kSecondaryTableSize; j++) { | |
| 443 secondary_[j].key = isolate()->heap()->empty_string(); | |
| 444 secondary_[j].map = NULL; | |
| 445 secondary_[j].value = empty; | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 | |
| 450 void StubCache::CollectMatchingMaps(SmallMapList* types, | |
| 451 Handle<Name> name, | |
| 452 Code::Flags flags, | |
| 453 Handle<Context> native_context, | |
| 454 Zone* zone) { | |
| 455 for (int i = 0; i < kPrimaryTableSize; i++) { | |
| 456 if (primary_[i].key == *name) { | |
| 457 Map* map = primary_[i].map; | |
| 458 // Map can be NULL, if the stub is constant function call | |
| 459 // with a primitive receiver. | |
| 460 if (map == NULL) continue; | |
| 461 | |
| 462 int offset = PrimaryOffset(*name, flags, map); | |
| 463 if (entry(primary_, offset) == &primary_[i] && | |
| 464 !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { | |
| 465 types->AddMapIfMissing(Handle<Map>(map), zone); | |
| 466 } | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 for (int i = 0; i < kSecondaryTableSize; i++) { | |
| 471 if (secondary_[i].key == *name) { | |
| 472 Map* map = secondary_[i].map; | |
| 473 // Map can be NULL, if the stub is constant function call | |
| 474 // with a primitive receiver. | |
| 475 if (map == NULL) continue; | |
| 476 | |
| 477 // Lookup in primary table and skip duplicates. | |
| 478 int primary_offset = PrimaryOffset(*name, flags, map); | |
| 479 | |
| 480 // Lookup in secondary table and add matches. | |
| 481 int offset = SecondaryOffset(*name, flags, primary_offset); | |
| 482 if (entry(secondary_, offset) == &secondary_[i] && | |
| 483 !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { | |
| 484 types->AddMapIfMissing(Handle<Map>(map), zone); | |
| 485 } | |
| 486 } | |
| 487 } | |
| 488 } | |
| 489 | |
| 490 | |
| 491 // ------------------------------------------------------------------------ | |
| 492 // StubCompiler implementation. | |
| 493 | |
| 494 | |
| 495 RUNTIME_FUNCTION(StoreCallbackProperty) { | |
| 496 Handle<JSObject> receiver = args.at<JSObject>(0); | |
| 497 Handle<JSObject> holder = args.at<JSObject>(1); | |
| 498 Handle<ExecutableAccessorInfo> callback = args.at<ExecutableAccessorInfo>(2); | |
| 499 Handle<Name> name = args.at<Name>(3); | |
| 500 Handle<Object> value = args.at<Object>(4); | |
| 501 HandleScope scope(isolate); | |
| 502 | |
| 503 DCHECK(callback->IsCompatibleReceiver(*receiver)); | |
| 504 | |
| 505 Address setter_address = v8::ToCData<Address>(callback->setter()); | |
| 506 v8::AccessorNameSetterCallback fun = | |
| 507 FUNCTION_CAST<v8::AccessorNameSetterCallback>(setter_address); | |
| 508 DCHECK(fun != NULL); | |
| 509 | |
| 510 LOG(isolate, ApiNamedPropertyAccess("store", *receiver, *name)); | |
| 511 PropertyCallbackArguments custom_args(isolate, callback->data(), *receiver, | |
| 512 *holder); | |
| 513 custom_args.Call(fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); | |
| 514 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); | |
| 515 return *value; | |
| 516 } | |
| 517 | |
| 518 | |
| 519 /** | |
| 520 * Attempts to load a property with an interceptor (which must be present), | |
| 521 * but doesn't search the prototype chain. | |
| 522 * | |
| 523 * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't | |
| 524 * provide any value for the given name. | |
| 525 */ | |
| 526 RUNTIME_FUNCTION(LoadPropertyWithInterceptorOnly) { | |
| 527 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength); | |
| 528 Handle<Name> name_handle = | |
| 529 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex); | |
| 530 Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>( | |
| 531 NamedLoadHandlerCompiler::kInterceptorArgsInfoIndex); | |
| 532 | |
| 533 // TODO(rossberg): Support symbols in the API. | |
| 534 if (name_handle->IsSymbol()) | |
| 535 return isolate->heap()->no_interceptor_result_sentinel(); | |
| 536 Handle<String> name = Handle<String>::cast(name_handle); | |
| 537 | |
| 538 Address getter_address = v8::ToCData<Address>(interceptor_info->getter()); | |
| 539 v8::NamedPropertyGetterCallback getter = | |
| 540 FUNCTION_CAST<v8::NamedPropertyGetterCallback>(getter_address); | |
| 541 DCHECK(getter != NULL); | |
| 542 | |
| 543 Handle<JSObject> receiver = | |
| 544 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex); | |
| 545 Handle<JSObject> holder = | |
| 546 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex); | |
| 547 PropertyCallbackArguments callback_args( | |
| 548 isolate, interceptor_info->data(), *receiver, *holder); | |
| 549 { | |
| 550 // Use the interceptor getter. | |
| 551 HandleScope scope(isolate); | |
| 552 v8::Handle<v8::Value> r = | |
| 553 callback_args.Call(getter, v8::Utils::ToLocal(name)); | |
| 554 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); | |
| 555 if (!r.IsEmpty()) { | |
| 556 Handle<Object> result = v8::Utils::OpenHandle(*r); | |
| 557 result->VerifyApiCallResultType(); | |
| 558 return *v8::Utils::OpenHandle(*r); | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 return isolate->heap()->no_interceptor_result_sentinel(); | |
| 563 } | |
| 564 | |
| 565 | |
| 566 static Object* ThrowReferenceError(Isolate* isolate, Name* name) { | |
| 567 // If the load is non-contextual, just return the undefined result. | |
| 568 // Note that both keyed and non-keyed loads may end up here. | |
| 569 HandleScope scope(isolate); | |
| 570 LoadIC ic(IC::NO_EXTRA_FRAME, isolate); | |
| 571 if (ic.contextual_mode() != CONTEXTUAL) { | |
| 572 return isolate->heap()->undefined_value(); | |
| 573 } | |
| 574 | |
| 575 // Throw a reference error. | |
| 576 Handle<Name> name_handle(name); | |
| 577 Handle<Object> error = | |
| 578 isolate->factory()->NewReferenceError("not_defined", | |
| 579 HandleVector(&name_handle, 1)); | |
| 580 return isolate->Throw(*error); | |
| 581 } | |
| 582 | |
| 583 | |
| 584 /** | |
| 585 * Loads a property with an interceptor performing post interceptor | |
| 586 * lookup if interceptor failed. | |
| 587 */ | |
| 588 RUNTIME_FUNCTION(LoadPropertyWithInterceptor) { | |
| 589 HandleScope scope(isolate); | |
| 590 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength); | |
| 591 Handle<Name> name = | |
| 592 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex); | |
| 593 Handle<JSObject> receiver = | |
| 594 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex); | |
| 595 Handle<JSObject> holder = | |
| 596 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex); | |
| 597 | |
| 598 Handle<Object> result; | |
| 599 LookupIterator it(receiver, name, holder); | |
| 600 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 601 isolate, result, JSObject::GetProperty(&it)); | |
| 602 | |
| 603 if (it.IsFound()) return *result; | |
| 604 | |
| 605 return ThrowReferenceError(isolate, Name::cast(args[0])); | |
| 606 } | |
| 607 | |
| 608 | |
| 609 RUNTIME_FUNCTION(StorePropertyWithInterceptor) { | |
| 610 HandleScope scope(isolate); | |
| 611 DCHECK(args.length() == 3); | |
| 612 StoreIC ic(IC::NO_EXTRA_FRAME, isolate); | |
| 613 Handle<JSObject> receiver = args.at<JSObject>(0); | |
| 614 Handle<Name> name = args.at<Name>(1); | |
| 615 Handle<Object> value = args.at<Object>(2); | |
| 616 #ifdef DEBUG | |
| 617 PrototypeIterator iter(isolate, receiver, | |
| 618 PrototypeIterator::START_AT_RECEIVER); | |
| 619 bool found = false; | |
| 620 while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { | |
| 621 Handle<Object> current = PrototypeIterator::GetCurrent(iter); | |
| 622 if (current->IsJSObject() && | |
| 623 Handle<JSObject>::cast(current)->HasNamedInterceptor()) { | |
| 624 found = true; | |
| 625 break; | |
| 626 } | |
| 627 } | |
| 628 DCHECK(found); | |
| 629 #endif | |
| 630 Handle<Object> result; | |
| 631 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 632 isolate, result, | |
| 633 JSObject::SetProperty(receiver, name, value, ic.strict_mode())); | |
| 634 return *result; | |
| 635 } | |
| 636 | |
| 637 | |
| 638 RUNTIME_FUNCTION(LoadElementWithInterceptor) { | |
| 639 HandleScope scope(isolate); | |
| 640 Handle<JSObject> receiver = args.at<JSObject>(0); | |
| 641 DCHECK(args.smi_at(1) >= 0); | |
| 642 uint32_t index = args.smi_at(1); | |
| 643 Handle<Object> result; | |
| 644 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
| 645 isolate, result, | |
| 646 JSObject::GetElementWithInterceptor(receiver, receiver, index)); | |
| 647 return *result; | |
| 648 } | |
| 649 | |
| 650 | |
| 651 Handle<Code> PropertyICCompiler::CompileLoadInitialize(Code::Flags flags) { | |
| 652 LoadIC::GenerateInitialize(masm()); | |
| 653 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadInitialize"); | |
| 654 PROFILE(isolate(), | |
| 655 CodeCreateEvent(Logger::LOAD_INITIALIZE_TAG, *code, 0)); | |
| 656 return code; | |
| 657 } | |
| 658 | |
| 659 | |
| 660 Handle<Code> PropertyICCompiler::CompileLoadPreMonomorphic(Code::Flags flags) { | |
| 661 LoadIC::GeneratePreMonomorphic(masm()); | |
| 662 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadPreMonomorphic"); | |
| 663 PROFILE(isolate(), | |
| 664 CodeCreateEvent(Logger::LOAD_PREMONOMORPHIC_TAG, *code, 0)); | |
| 665 return code; | |
| 666 } | |
| 667 | |
| 668 | |
| 669 Handle<Code> PropertyICCompiler::CompileLoadMegamorphic(Code::Flags flags) { | |
| 670 LoadIC::GenerateMegamorphic(masm()); | |
| 671 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadMegamorphic"); | |
| 672 PROFILE(isolate(), | |
| 673 CodeCreateEvent(Logger::LOAD_MEGAMORPHIC_TAG, *code, 0)); | |
| 674 return code; | |
| 675 } | |
| 676 | |
| 677 | |
| 678 Handle<Code> PropertyICCompiler::CompileStoreInitialize(Code::Flags flags) { | |
| 679 StoreIC::GenerateInitialize(masm()); | |
| 680 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreInitialize"); | |
| 681 PROFILE(isolate(), | |
| 682 CodeCreateEvent(Logger::STORE_INITIALIZE_TAG, *code, 0)); | |
| 683 return code; | |
| 684 } | |
| 685 | |
| 686 | |
| 687 Handle<Code> PropertyICCompiler::CompileStorePreMonomorphic(Code::Flags flags) { | |
| 688 StoreIC::GeneratePreMonomorphic(masm()); | |
| 689 Handle<Code> code = GetCodeWithFlags(flags, "CompileStorePreMonomorphic"); | |
| 690 PROFILE(isolate(), | |
| 691 CodeCreateEvent(Logger::STORE_PREMONOMORPHIC_TAG, *code, 0)); | |
| 692 return code; | |
| 693 } | |
| 694 | |
| 695 | |
| 696 Handle<Code> PropertyICCompiler::CompileStoreGeneric(Code::Flags flags) { | |
| 697 ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); | |
| 698 StrictMode strict_mode = StoreIC::GetStrictMode(extra_state); | |
| 699 StoreIC::GenerateRuntimeSetProperty(masm(), strict_mode); | |
| 700 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreGeneric"); | |
| 701 PROFILE(isolate(), | |
| 702 CodeCreateEvent(Logger::STORE_GENERIC_TAG, *code, 0)); | |
| 703 return code; | |
| 704 } | |
| 705 | |
| 706 | |
| 707 Handle<Code> PropertyICCompiler::CompileStoreMegamorphic(Code::Flags flags) { | |
| 708 StoreIC::GenerateMegamorphic(masm()); | |
| 709 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreMegamorphic"); | |
| 710 PROFILE(isolate(), | |
| 711 CodeCreateEvent(Logger::STORE_MEGAMORPHIC_TAG, *code, 0)); | |
| 712 return code; | |
| 713 } | |
| 714 | |
| 715 | |
| 716 #undef CALL_LOGGER_TAG | |
| 717 | |
| 718 | |
| 719 Handle<Code> PropertyAccessCompiler::GetCodeWithFlags(Code::Flags flags, | |
| 720 const char* name) { | |
| 721 // Create code object in the heap. | |
| 722 CodeDesc desc; | |
| 723 masm()->GetCode(&desc); | |
| 724 Handle<Code> code = factory()->NewCode(desc, flags, masm()->CodeObject()); | |
| 725 if (code->IsCodeStubOrIC()) code->set_stub_key(CodeStub::NoCacheKey()); | |
| 726 #ifdef ENABLE_DISASSEMBLER | |
| 727 if (FLAG_print_code_stubs) { | |
| 728 OFStream os(stdout); | |
| 729 code->Disassemble(name, os); | |
| 730 } | |
| 731 #endif | |
| 732 return code; | |
| 733 } | |
| 734 | |
| 735 | |
| 736 Handle<Code> PropertyAccessCompiler::GetCodeWithFlags(Code::Flags flags, | |
| 737 Handle<Name> name) { | |
| 738 return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) | |
| 739 ? GetCodeWithFlags(flags, Handle<String>::cast(name)->ToCString().get()) | |
| 740 : GetCodeWithFlags(flags, NULL); | |
| 741 } | |
| 742 | |
| 743 | |
| 744 #define __ ACCESS_MASM(masm()) | |
| 745 | |
| 746 | |
| 747 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, | |
| 748 Handle<Name> name, | |
| 749 Label* miss) { | |
| 750 PrototypeCheckType check_type = CHECK_ALL_MAPS; | |
| 751 int function_index = -1; | |
| 752 if (type()->Is(HeapType::String())) { | |
| 753 function_index = Context::STRING_FUNCTION_INDEX; | |
| 754 } else if (type()->Is(HeapType::Symbol())) { | |
| 755 function_index = Context::SYMBOL_FUNCTION_INDEX; | |
| 756 } else if (type()->Is(HeapType::Number())) { | |
| 757 function_index = Context::NUMBER_FUNCTION_INDEX; | |
| 758 } else if (type()->Is(HeapType::Boolean())) { | |
| 759 function_index = Context::BOOLEAN_FUNCTION_INDEX; | |
| 760 } else { | |
| 761 check_type = SKIP_RECEIVER; | |
| 762 } | |
| 763 | |
| 764 if (check_type == CHECK_ALL_MAPS) { | |
| 765 GenerateDirectLoadGlobalFunctionPrototype( | |
| 766 masm(), function_index, scratch1(), miss); | |
| 767 Object* function = isolate()->native_context()->get(function_index); | |
| 768 Object* prototype = JSFunction::cast(function)->instance_prototype(); | |
| 769 set_type_for_object(handle(prototype, isolate())); | |
| 770 object_reg = scratch1(); | |
| 771 } | |
| 772 | |
| 773 // Check that the maps starting from the prototype haven't changed. | |
| 774 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name, | |
| 775 miss, check_type); | |
| 776 } | |
| 777 | |
| 778 | |
| 779 // Frontend for store uses the name register. It has to be restored before a | |
| 780 // miss. | |
| 781 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg, | |
| 782 Handle<Name> name, | |
| 783 Label* miss) { | |
| 784 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name, | |
| 785 miss, SKIP_RECEIVER); | |
| 786 } | |
| 787 | |
| 788 | |
| 789 bool PropertyICCompiler::IncludesNumberType(TypeHandleList* types) { | |
| 790 for (int i = 0; i < types->length(); ++i) { | |
| 791 if (types->at(i)->Is(HeapType::Number())) return true; | |
| 792 } | |
| 793 return false; | |
| 794 } | |
| 795 | |
| 796 | |
| 797 Register PropertyHandlerCompiler::Frontend(Register object_reg, | |
| 798 Handle<Name> name) { | |
| 799 Label miss; | |
| 800 Register reg = FrontendHeader(object_reg, name, &miss); | |
| 801 FrontendFooter(name, &miss); | |
| 802 return reg; | |
| 803 } | |
| 804 | |
| 805 | |
| 806 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name, | |
| 807 Label* miss, | |
| 808 Register scratch1, | |
| 809 Register scratch2) { | |
| 810 Register holder_reg; | |
| 811 Handle<Map> last_map; | |
| 812 if (holder().is_null()) { | |
| 813 holder_reg = receiver(); | |
| 814 last_map = IC::TypeToMap(*type(), isolate()); | |
| 815 // If |type| has null as its prototype, |holder()| is | |
| 816 // Handle<JSObject>::null(). | |
| 817 DCHECK(last_map->prototype() == isolate()->heap()->null_value()); | |
| 818 } else { | |
| 819 holder_reg = FrontendHeader(receiver(), name, miss); | |
| 820 last_map = handle(holder()->map()); | |
| 821 } | |
| 822 | |
| 823 if (last_map->is_dictionary_map()) { | |
| 824 if (last_map->IsJSGlobalObjectMap()) { | |
| 825 Handle<JSGlobalObject> global = | |
| 826 holder().is_null() | |
| 827 ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value()) | |
| 828 : Handle<JSGlobalObject>::cast(holder()); | |
| 829 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss); | |
| 830 } else { | |
| 831 if (!name->IsUniqueName()) { | |
| 832 DCHECK(name->IsString()); | |
| 833 name = factory()->InternalizeString(Handle<String>::cast(name)); | |
| 834 } | |
| 835 DCHECK(holder().is_null() || | |
| 836 holder()->property_dictionary()->FindEntry(name) == | |
| 837 NameDictionary::kNotFound); | |
| 838 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1, | |
| 839 scratch2); | |
| 840 } | |
| 841 } | |
| 842 } | |
| 843 | |
| 844 | |
| 845 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name, | |
| 846 FieldIndex field) { | |
| 847 Register reg = Frontend(receiver(), name); | |
| 848 __ Move(receiver(), reg); | |
| 849 LoadFieldStub stub(isolate(), field); | |
| 850 GenerateTailCall(masm(), stub.GetCode()); | |
| 851 return GetCode(kind(), Code::FAST, name); | |
| 852 } | |
| 853 | |
| 854 | |
| 855 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name, | |
| 856 int constant_index) { | |
| 857 Register reg = Frontend(receiver(), name); | |
| 858 __ Move(receiver(), reg); | |
| 859 LoadConstantStub stub(isolate(), constant_index); | |
| 860 GenerateTailCall(masm(), stub.GetCode()); | |
| 861 return GetCode(kind(), Code::FAST, name); | |
| 862 } | |
| 863 | |
| 864 | |
| 865 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent( | |
| 866 Handle<Name> name) { | |
| 867 Label miss; | |
| 868 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3()); | |
| 869 GenerateLoadConstant(isolate()->factory()->undefined_value()); | |
| 870 FrontendFooter(name, &miss); | |
| 871 return GetCode(kind(), Code::FAST, name); | |
| 872 } | |
| 873 | |
| 874 | |
| 875 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( | |
| 876 Handle<Name> name, Handle<ExecutableAccessorInfo> callback) { | |
| 877 Register reg = Frontend(receiver(), name); | |
| 878 GenerateLoadCallback(reg, callback); | |
| 879 return GetCode(kind(), Code::FAST, name); | |
| 880 } | |
| 881 | |
| 882 | |
| 883 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( | |
| 884 Handle<Name> name, const CallOptimization& call_optimization) { | |
| 885 DCHECK(call_optimization.is_simple_api_call()); | |
| 886 Frontend(receiver(), name); | |
| 887 Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate()); | |
| 888 GenerateFastApiCall( | |
| 889 masm(), call_optimization, receiver_map, | |
| 890 receiver(), scratch1(), false, 0, NULL); | |
| 891 return GetCode(kind(), Code::FAST, name); | |
| 892 } | |
| 893 | |
| 894 | |
| 895 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( | |
| 896 LookupIterator* it) { | |
| 897 // So far the most popular follow ups for interceptor loads are FIELD and | |
| 898 // ExecutableAccessorInfo, so inline only them. Other cases may be added | |
| 899 // later. | |
| 900 bool inline_followup = it->state() == LookupIterator::PROPERTY; | |
| 901 if (inline_followup) { | |
| 902 switch (it->property_kind()) { | |
| 903 case LookupIterator::DATA: | |
| 904 inline_followup = it->property_details().type() == FIELD; | |
| 905 break; | |
| 906 case LookupIterator::ACCESSOR: { | |
| 907 Handle<Object> accessors = it->GetAccessors(); | |
| 908 inline_followup = accessors->IsExecutableAccessorInfo(); | |
| 909 if (!inline_followup) break; | |
| 910 Handle<ExecutableAccessorInfo> info = | |
| 911 Handle<ExecutableAccessorInfo>::cast(accessors); | |
| 912 inline_followup = info->getter() != NULL && | |
| 913 ExecutableAccessorInfo::IsCompatibleReceiverType( | |
| 914 isolate(), info, type()); | |
| 915 } | |
| 916 } | |
| 917 } | |
| 918 | |
| 919 Register reg = Frontend(receiver(), it->name()); | |
| 920 if (inline_followup) { | |
| 921 // TODO(368): Compile in the whole chain: all the interceptors in | |
| 922 // prototypes and ultimate answer. | |
| 923 GenerateLoadInterceptorWithFollowup(it, reg); | |
| 924 } else { | |
| 925 GenerateLoadInterceptor(reg); | |
| 926 } | |
| 927 return GetCode(kind(), Code::FAST, it->name()); | |
| 928 } | |
| 929 | |
| 930 | |
| 931 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( | |
| 932 LookupIterator* it, Register interceptor_reg) { | |
| 933 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>()); | |
| 934 | |
| 935 set_type_for_object(holder()); | |
| 936 set_holder(real_named_property_holder); | |
| 937 Register reg = Frontend(interceptor_reg, it->name()); | |
| 938 | |
| 939 switch (it->property_kind()) { | |
| 940 case LookupIterator::DATA: { | |
| 941 DCHECK_EQ(FIELD, it->property_details().type()); | |
| 942 __ Move(receiver(), reg); | |
| 943 LoadFieldStub stub(isolate(), it->GetFieldIndex()); | |
| 944 GenerateTailCall(masm(), stub.GetCode()); | |
| 945 break; | |
| 946 } | |
| 947 case LookupIterator::ACCESSOR: | |
| 948 Handle<ExecutableAccessorInfo> info = | |
| 949 Handle<ExecutableAccessorInfo>::cast(it->GetAccessors()); | |
| 950 DCHECK_NE(NULL, info->getter()); | |
| 951 GenerateLoadCallback(reg, info); | |
| 952 } | |
| 953 } | |
| 954 | |
| 955 | |
| 956 Handle<Code> PropertyICCompiler::CompileMonomorphic(Handle<HeapType> type, | |
| 957 Handle<Code> handler, | |
| 958 Handle<Name> name, | |
| 959 IcCheckType check) { | |
| 960 TypeHandleList types(1); | |
| 961 CodeHandleList handlers(1); | |
| 962 types.Add(type); | |
| 963 handlers.Add(handler); | |
| 964 Code::StubType stub_type = handler->type(); | |
| 965 return CompilePolymorphic(&types, &handlers, name, stub_type, check); | |
| 966 } | |
| 967 | |
| 968 | |
| 969 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter( | |
| 970 Handle<Name> name, Handle<JSFunction> getter) { | |
| 971 Frontend(receiver(), name); | |
| 972 GenerateLoadViaGetter(masm(), type(), receiver(), getter); | |
| 973 return GetCode(kind(), Code::FAST, name); | |
| 974 } | |
| 975 | |
| 976 | |
| 977 // TODO(verwaest): Cleanup. holder() is actually the receiver. | |
| 978 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition( | |
| 979 Handle<Map> transition, Handle<Name> name) { | |
| 980 Label miss, slow; | |
| 981 | |
| 982 // Ensure no transitions to deprecated maps are followed. | |
| 983 __ CheckMapDeprecated(transition, scratch1(), &miss); | |
| 984 | |
| 985 // Check that we are allowed to write this. | |
| 986 bool is_nonexistent = holder()->map() == transition->GetBackPointer(); | |
| 987 if (is_nonexistent) { | |
| 988 // Find the top object. | |
| 989 Handle<JSObject> last; | |
| 990 PrototypeIterator iter(isolate(), holder()); | |
| 991 while (!iter.IsAtEnd()) { | |
| 992 last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); | |
| 993 iter.Advance(); | |
| 994 } | |
| 995 if (!last.is_null()) set_holder(last); | |
| 996 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2()); | |
| 997 } else { | |
| 998 FrontendHeader(receiver(), name, &miss); | |
| 999 DCHECK(holder()->HasFastProperties()); | |
| 1000 } | |
| 1001 | |
| 1002 GenerateStoreTransition(transition, name, receiver(), this->name(), value(), | |
| 1003 scratch1(), scratch2(), scratch3(), &miss, &slow); | |
| 1004 | |
| 1005 GenerateRestoreName(&miss, name); | |
| 1006 TailCallBuiltin(masm(), MissBuiltin(kind())); | |
| 1007 | |
| 1008 GenerateRestoreName(&slow, name); | |
| 1009 TailCallBuiltin(masm(), SlowBuiltin(kind())); | |
| 1010 return GetCode(kind(), Code::FAST, name); | |
| 1011 } | |
| 1012 | |
| 1013 | |
| 1014 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) { | |
| 1015 Label miss; | |
| 1016 GenerateStoreField(it, value(), &miss); | |
| 1017 __ bind(&miss); | |
| 1018 TailCallBuiltin(masm(), MissBuiltin(kind())); | |
| 1019 return GetCode(kind(), Code::FAST, it->name()); | |
| 1020 } | |
| 1021 | |
| 1022 | |
| 1023 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter( | |
| 1024 Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) { | |
| 1025 Frontend(receiver(), name); | |
| 1026 GenerateStoreViaSetter(masm(), type(), receiver(), setter); | |
| 1027 | |
| 1028 return GetCode(kind(), Code::FAST, name); | |
| 1029 } | |
| 1030 | |
| 1031 | |
| 1032 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( | |
| 1033 Handle<JSObject> object, Handle<Name> name, | |
| 1034 const CallOptimization& call_optimization) { | |
| 1035 Frontend(receiver(), name); | |
| 1036 Register values[] = { value() }; | |
| 1037 GenerateFastApiCall( | |
| 1038 masm(), call_optimization, handle(object->map()), | |
| 1039 receiver(), scratch1(), true, 1, values); | |
| 1040 return GetCode(kind(), Code::FAST, name); | |
| 1041 } | |
| 1042 | |
| 1043 | |
| 1044 Handle<Code> PropertyICCompiler::CompileKeyedStoreMonomorphic( | |
| 1045 Handle<Map> receiver_map, KeyedAccessStoreMode store_mode) { | |
| 1046 ElementsKind elements_kind = receiver_map->elements_kind(); | |
| 1047 bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
| 1048 Handle<Code> stub; | |
| 1049 if (receiver_map->has_fast_elements() || | |
| 1050 receiver_map->has_external_array_elements() || | |
| 1051 receiver_map->has_fixed_typed_array_elements()) { | |
| 1052 stub = StoreFastElementStub(isolate(), is_jsarray, elements_kind, | |
| 1053 store_mode).GetCode(); | |
| 1054 } else { | |
| 1055 stub = StoreElementStub(isolate(), is_jsarray, elements_kind, store_mode) | |
| 1056 .GetCode(); | |
| 1057 } | |
| 1058 | |
| 1059 __ DispatchMap(receiver(), scratch1(), receiver_map, stub, DO_SMI_CHECK); | |
| 1060 | |
| 1061 TailCallBuiltin(masm(), Builtins::kKeyedStoreIC_Miss); | |
| 1062 | |
| 1063 return GetCode(kind(), Code::NORMAL, factory()->empty_string()); | |
| 1064 } | |
| 1065 | |
| 1066 | |
| 1067 #undef __ | |
| 1068 | |
| 1069 | |
| 1070 void PropertyAccessCompiler::TailCallBuiltin(MacroAssembler* masm, | |
| 1071 Builtins::Name name) { | |
| 1072 Handle<Code> code(masm->isolate()->builtins()->builtin(name)); | |
| 1073 GenerateTailCall(masm, code); | |
| 1074 } | |
| 1075 | |
| 1076 | |
| 1077 Register* PropertyAccessCompiler::GetCallingConvention(Code::Kind kind) { | |
| 1078 if (kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC) { | |
| 1079 return load_calling_convention(); | |
| 1080 } | |
| 1081 DCHECK(kind == Code::STORE_IC || kind == Code::KEYED_STORE_IC); | |
| 1082 return store_calling_convention(); | |
| 1083 } | |
| 1084 | |
| 1085 | |
| 1086 Handle<Code> PropertyICCompiler::GetCode(Code::Kind kind, Code::StubType type, | |
| 1087 Handle<Name> name, | |
| 1088 InlineCacheState state) { | |
| 1089 Code::Flags flags = | |
| 1090 Code::ComputeFlags(kind, state, extra_ic_state_, type, cache_holder()); | |
| 1091 Handle<Code> code = GetCodeWithFlags(flags, name); | |
| 1092 IC::RegisterWeakMapDependency(code); | |
| 1093 PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, *name)); | |
| 1094 return code; | |
| 1095 } | |
| 1096 | |
| 1097 | |
| 1098 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind, | |
| 1099 Code::StubType type, | |
| 1100 Handle<Name> name) { | |
| 1101 Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder()); | |
| 1102 Handle<Code> code = GetCodeWithFlags(flags, name); | |
| 1103 PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name)); | |
| 1104 return code; | |
| 1105 } | |
| 1106 | |
| 1107 | |
| 1108 void ElementHandlerCompiler::CompileElementHandlers( | |
| 1109 MapHandleList* receiver_maps, CodeHandleList* handlers) { | |
| 1110 for (int i = 0; i < receiver_maps->length(); ++i) { | |
| 1111 Handle<Map> receiver_map = receiver_maps->at(i); | |
| 1112 Handle<Code> cached_stub; | |
| 1113 | |
| 1114 if ((receiver_map->instance_type() & kNotStringTag) == 0) { | |
| 1115 cached_stub = isolate()->builtins()->KeyedLoadIC_String(); | |
| 1116 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { | |
| 1117 cached_stub = isolate()->builtins()->KeyedLoadIC_Slow(); | |
| 1118 } else { | |
| 1119 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
| 1120 ElementsKind elements_kind = receiver_map->elements_kind(); | |
| 1121 | |
| 1122 if (IsFastElementsKind(elements_kind) || | |
| 1123 IsExternalArrayElementsKind(elements_kind) || | |
| 1124 IsFixedTypedArrayElementsKind(elements_kind)) { | |
| 1125 cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind) | |
| 1126 .GetCode(); | |
| 1127 } else if (elements_kind == SLOPPY_ARGUMENTS_ELEMENTS) { | |
| 1128 cached_stub = isolate()->builtins()->KeyedLoadIC_SloppyArguments(); | |
| 1129 } else { | |
| 1130 DCHECK(elements_kind == DICTIONARY_ELEMENTS); | |
| 1131 cached_stub = LoadDictionaryElementStub(isolate()).GetCode(); | |
| 1132 } | |
| 1133 } | |
| 1134 | |
| 1135 handlers->Add(cached_stub); | |
| 1136 } | |
| 1137 } | |
| 1138 | |
| 1139 | |
| 1140 Handle<Code> PropertyICCompiler::CompileKeyedStorePolymorphic( | |
| 1141 MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode) { | |
| 1142 // Collect MONOMORPHIC stubs for all |receiver_maps|. | |
| 1143 CodeHandleList handlers(receiver_maps->length()); | |
| 1144 MapHandleList transitioned_maps(receiver_maps->length()); | |
| 1145 for (int i = 0; i < receiver_maps->length(); ++i) { | |
| 1146 Handle<Map> receiver_map(receiver_maps->at(i)); | |
| 1147 Handle<Code> cached_stub; | |
| 1148 Handle<Map> transitioned_map = | |
| 1149 receiver_map->FindTransitionedMap(receiver_maps); | |
| 1150 | |
| 1151 // TODO(mvstanton): The code below is doing pessimistic elements | |
| 1152 // transitions. I would like to stop doing that and rely on Allocation Site | |
| 1153 // Tracking to do a better job of ensuring the data types are what they need | |
| 1154 // to be. Not all the elements are in place yet, pessimistic elements | |
| 1155 // transitions are still important for performance. | |
| 1156 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
| 1157 ElementsKind elements_kind = receiver_map->elements_kind(); | |
| 1158 if (!transitioned_map.is_null()) { | |
| 1159 cached_stub = | |
| 1160 ElementsTransitionAndStoreStub(isolate(), elements_kind, | |
| 1161 transitioned_map->elements_kind(), | |
| 1162 is_js_array, store_mode).GetCode(); | |
| 1163 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { | |
| 1164 cached_stub = isolate()->builtins()->KeyedStoreIC_Slow(); | |
| 1165 } else { | |
| 1166 if (receiver_map->has_fast_elements() || | |
| 1167 receiver_map->has_external_array_elements() || | |
| 1168 receiver_map->has_fixed_typed_array_elements()) { | |
| 1169 cached_stub = StoreFastElementStub(isolate(), is_js_array, | |
| 1170 elements_kind, store_mode).GetCode(); | |
| 1171 } else { | |
| 1172 cached_stub = StoreElementStub(isolate(), is_js_array, elements_kind, | |
| 1173 store_mode).GetCode(); | |
| 1174 } | |
| 1175 } | |
| 1176 DCHECK(!cached_stub.is_null()); | |
| 1177 handlers.Add(cached_stub); | |
| 1178 transitioned_maps.Add(transitioned_map); | |
| 1179 } | |
| 1180 | |
| 1181 Handle<Code> code = CompileKeyedStorePolymorphic(receiver_maps, &handlers, | |
| 1182 &transitioned_maps); | |
| 1183 isolate()->counters()->keyed_store_polymorphic_stubs()->Increment(); | |
| 1184 PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, 0)); | |
| 1185 return code; | |
| 1186 } | |
| 1187 | |
| 1188 | |
| 1189 void ElementHandlerCompiler::GenerateStoreDictionaryElement( | |
| 1190 MacroAssembler* masm) { | |
| 1191 KeyedStoreIC::GenerateSlow(masm); | |
| 1192 } | |
| 1193 | |
| 1194 | |
| 1195 CallOptimization::CallOptimization(Handle<JSFunction> function) { | |
| 1196 Initialize(function); | |
| 1197 } | |
| 1198 | |
| 1199 | |
| 1200 Handle<JSObject> CallOptimization::LookupHolderOfExpectedType( | |
| 1201 Handle<Map> object_map, | |
| 1202 HolderLookup* holder_lookup) const { | |
| 1203 DCHECK(is_simple_api_call()); | |
| 1204 if (!object_map->IsJSObjectMap()) { | |
| 1205 *holder_lookup = kHolderNotFound; | |
| 1206 return Handle<JSObject>::null(); | |
| 1207 } | |
| 1208 if (expected_receiver_type_.is_null() || | |
| 1209 expected_receiver_type_->IsTemplateFor(*object_map)) { | |
| 1210 *holder_lookup = kHolderIsReceiver; | |
| 1211 return Handle<JSObject>::null(); | |
| 1212 } | |
| 1213 while (true) { | |
| 1214 if (!object_map->prototype()->IsJSObject()) break; | |
| 1215 Handle<JSObject> prototype(JSObject::cast(object_map->prototype())); | |
| 1216 if (!prototype->map()->is_hidden_prototype()) break; | |
| 1217 object_map = handle(prototype->map()); | |
| 1218 if (expected_receiver_type_->IsTemplateFor(*object_map)) { | |
| 1219 *holder_lookup = kHolderFound; | |
| 1220 return prototype; | |
| 1221 } | |
| 1222 } | |
| 1223 *holder_lookup = kHolderNotFound; | |
| 1224 return Handle<JSObject>::null(); | |
| 1225 } | |
| 1226 | |
| 1227 | |
| 1228 bool CallOptimization::IsCompatibleReceiver(Handle<Object> receiver, | |
| 1229 Handle<JSObject> holder) const { | |
| 1230 DCHECK(is_simple_api_call()); | |
| 1231 if (!receiver->IsJSObject()) return false; | |
| 1232 Handle<Map> map(JSObject::cast(*receiver)->map()); | |
| 1233 HolderLookup holder_lookup; | |
| 1234 Handle<JSObject> api_holder = | |
| 1235 LookupHolderOfExpectedType(map, &holder_lookup); | |
| 1236 switch (holder_lookup) { | |
| 1237 case kHolderNotFound: | |
| 1238 return false; | |
| 1239 case kHolderIsReceiver: | |
| 1240 return true; | |
| 1241 case kHolderFound: | |
| 1242 if (api_holder.is_identical_to(holder)) return true; | |
| 1243 // Check if holder is in prototype chain of api_holder. | |
| 1244 { | |
| 1245 JSObject* object = *api_holder; | |
| 1246 while (true) { | |
| 1247 Object* prototype = object->map()->prototype(); | |
| 1248 if (!prototype->IsJSObject()) return false; | |
| 1249 if (prototype == *holder) return true; | |
| 1250 object = JSObject::cast(prototype); | |
| 1251 } | |
| 1252 } | |
| 1253 break; | |
| 1254 } | |
| 1255 UNREACHABLE(); | |
| 1256 return false; | |
| 1257 } | |
| 1258 | |
| 1259 | |
| 1260 void CallOptimization::Initialize(Handle<JSFunction> function) { | |
| 1261 constant_function_ = Handle<JSFunction>::null(); | |
| 1262 is_simple_api_call_ = false; | |
| 1263 expected_receiver_type_ = Handle<FunctionTemplateInfo>::null(); | |
| 1264 api_call_info_ = Handle<CallHandlerInfo>::null(); | |
| 1265 | |
| 1266 if (function.is_null() || !function->is_compiled()) return; | |
| 1267 | |
| 1268 constant_function_ = function; | |
| 1269 AnalyzePossibleApiFunction(function); | |
| 1270 } | |
| 1271 | |
| 1272 | |
| 1273 void CallOptimization::AnalyzePossibleApiFunction(Handle<JSFunction> function) { | |
| 1274 if (!function->shared()->IsApiFunction()) return; | |
| 1275 Handle<FunctionTemplateInfo> info(function->shared()->get_api_func_data()); | |
| 1276 | |
| 1277 // Require a C++ callback. | |
| 1278 if (info->call_code()->IsUndefined()) return; | |
| 1279 api_call_info_ = | |
| 1280 Handle<CallHandlerInfo>(CallHandlerInfo::cast(info->call_code())); | |
| 1281 | |
| 1282 // Accept signatures that either have no restrictions at all or | |
| 1283 // only have restrictions on the receiver. | |
| 1284 if (!info->signature()->IsUndefined()) { | |
| 1285 Handle<SignatureInfo> signature = | |
| 1286 Handle<SignatureInfo>(SignatureInfo::cast(info->signature())); | |
| 1287 if (!signature->args()->IsUndefined()) return; | |
| 1288 if (!signature->receiver()->IsUndefined()) { | |
| 1289 expected_receiver_type_ = | |
| 1290 Handle<FunctionTemplateInfo>( | |
| 1291 FunctionTemplateInfo::cast(signature->receiver())); | |
| 1292 } | |
| 1293 } | |
| 1294 | |
| 1295 is_simple_api_call_ = true; | |
| 1296 } | |
| 1297 | |
| 1298 | |
| 1299 } } // namespace v8::internal | |
| OLD | NEW |