// Copyright 2011 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "api.h" #include "arguments.h" #include "ast.h" #include "code-stubs.h" #include "gdb-jit.h" #include "ic-inl.h" #include "stub-cache.h" #include "vm-state-inl.h" namespace v8 { namespace internal { // ----------------------------------------------------------------------- // StubCache implementation. StubCache::StubCache(Isolate* isolate) : isolate_(isolate) { ASSERT(isolate == Isolate::Current()); memset(primary_, 0, sizeof(primary_[0]) * StubCache::kPrimaryTableSize); memset(secondary_, 0, sizeof(secondary_[0]) * StubCache::kSecondaryTableSize); } void StubCache::Initialize(bool create_heap_objects) { ASSERT(IsPowerOf2(kPrimaryTableSize)); ASSERT(IsPowerOf2(kSecondaryTableSize)); if (create_heap_objects) { HandleScope scope; Clear(); } } Code* StubCache::Set(String* name, Map* map, Code* code) { // Get the flags from the code. Code::Flags flags = Code::RemoveTypeFromFlags(code->flags()); // Validate that the name does not move on scavenge, and that we // can use identity checks instead of string equality checks. ASSERT(!heap()->InNewSpace(name)); ASSERT(name->IsSymbol()); // The state bits are not important to the hash function because // the stub cache only contains monomorphic stubs. Make sure that // the bits are the least significant so they will be the ones // masked out. ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC); STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); // Make sure that the code type is not included in the hash. ASSERT(Code::ExtractTypeFromFlags(flags) == 0); // Compute the primary entry. int primary_offset = PrimaryOffset(name, flags, map); Entry* primary = entry(primary_, primary_offset); Code* hit = primary->value; // If the primary entry has useful data in it, we retire it to the // secondary cache before overwriting it. if (hit != isolate_->builtins()->builtin(Builtins::kIllegal)) { Code::Flags primary_flags = Code::RemoveTypeFromFlags(hit->flags()); int secondary_offset = SecondaryOffset(primary->key, primary_flags, primary_offset); Entry* secondary = entry(secondary_, secondary_offset); *secondary = *primary; } // Update primary cache. primary->key = name; primary->value = code; return code; } MaybeObject* StubCache::ComputeLoadNonexistent(String* name, JSObject* receiver) { ASSERT(receiver->IsGlobalObject() || receiver->HasFastProperties()); // If no global objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map // and we use the empty string for the map cache in that case. If // there are global objects involved, we need to check global // property cells in the stub and therefore the stub will be // specific to the name. String* cache_name = heap()->empty_string(); if (receiver->IsGlobalObject()) cache_name = name; JSObject* last = receiver; while (last->GetPrototype() != heap()->null_value()) { last = JSObject::cast(last->GetPrototype()); if (last->IsGlobalObject()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NONEXISTENT); Object* code = receiver->map()->FindInCodeCache(cache_name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadNonexistent(cache_name, receiver, last); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), cache_name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, cache_name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(cache_name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeLoadField(String* name, JSObject* receiver, JSObject* holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadField(receiver, holder, field_index, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeLoadCallback(String* name, JSObject* receiver, JSObject* holder, AccessorInfo* callback) { ASSERT(v8::ToCData
(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadCallback(name, receiver, holder, callback); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeLoadConstant(String* name, JSObject* receiver, JSObject* holder, Object* value) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadConstant(receiver, holder, value, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeLoadInterceptor(String* name, JSObject* receiver, JSObject* holder) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadInterceptor(receiver, holder, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeLoadNormal() { return isolate_->builtins()->builtin(Builtins::kLoadIC_Normal); } MaybeObject* StubCache::ComputeLoadGlobal(String* name, JSObject* receiver, GlobalObject* holder, JSGlobalPropertyCell* cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { LoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadField(String* name, JSObject* receiver, JSObject* holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadField(name, receiver, holder, field_index); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadConstant(String* name, JSObject* receiver, JSObject* holder, Object* value) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadConstant(name, receiver, holder, value); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadInterceptor(String* name, JSObject* receiver, JSObject* holder) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadInterceptor(receiver, holder, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadCallback(String* name, JSObject* receiver, JSObject* holder, AccessorInfo* callback) { ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadCallback(name, receiver, holder, callback); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadArrayLength(String* name, JSArray* receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); ASSERT(receiver->IsJSObject()); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadArrayLength(name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadStringLength(String* name, String* receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Map* map = receiver->map(); Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadStringLength(name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = map->UpdateCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadFunctionPrototype( String* name, JSFunction* receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedLoadStubCompiler compiler; { MaybeObject* maybe_code = compiler.CompileLoadFunctionPrototype(name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeStoreField(String* name, JSObject* receiver, int field_index, Map* transition, StrictModeFlag strict_mode) { PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreField(receiver, field_index, transition, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedLoadOrStoreElement( JSObject* receiver, bool is_store, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( is_store ? Code::KEYED_STORE_IC : Code::KEYED_LOAD_IC, NORMAL, strict_mode); String* name = is_store ? isolate()->heap()->KeyedStoreElementMonomorphic_symbol() : isolate()->heap()->KeyedLoadElementMonomorphic_symbol(); Object* maybe_code = receiver->map()->FindInCodeCache(name, flags); if (!maybe_code->IsUndefined()) return Code::cast(maybe_code); MaybeObject* maybe_new_code = NULL; Map* receiver_map = receiver->map(); if (is_store) { KeyedStoreStubCompiler compiler(strict_mode); maybe_new_code = compiler.CompileStoreElement(receiver_map); } else { KeyedLoadStubCompiler compiler; maybe_new_code = compiler.CompileLoadElement(receiver_map); } Code* code; if (!maybe_new_code->To(&code)) return maybe_new_code; if (is_store) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), 0)); } ASSERT(code->IsCode()); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } return code; } MaybeObject* StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return isolate_->builtins()->builtin((strict_mode == kStrictMode) ? Builtins::kStoreIC_Normal_Strict : Builtins::kStoreIC_Normal); } MaybeObject* StubCache::ComputeStoreGlobal(String* name, GlobalObject* receiver, JSGlobalPropertyCell* cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreGlobal(receiver, cell, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeStoreCallback( String* name, JSObject* receiver, AccessorInfo* callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreCallback(receiver, callback, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeStoreInterceptor( String* name, JSObject* receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreInterceptor(receiver, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver, int field_index, Map* transition, StrictModeFlag strict_mode) { PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { KeyedStoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreField(receiver, field_index, transition, name); if (!maybe_code->ToObject(&code)) return maybe_code; } PROFILE(isolate(), CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = receiver->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) MaybeObject* StubCache::ComputeCallConstant(int argc, InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, Object* object, JSObject* holder, JSFunction* function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(object, holder); JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_ic_state, cache_holder, in_loop, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { // If the function hasn't been compiled yet, we cannot do it now // because it may cause GC. To avoid this issue, we return an // internal error which will make sure we do not update any // caches. if (!function->is_compiled()) return Failure::InternalError(); // Compile the stub - only create stubs for fully compiled functions. CallStubCompiler compiler( argc, in_loop, kind, extra_ic_state, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallConstant(object, holder, function, name, check); if (!maybe_code->ToObject(&code)) return maybe_code; } Code::cast(code)->set_check_type(check); ASSERT_EQ(flags, Code::cast(code)->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = map_holder->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeCallField(int argc, InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, Object* object, JSObject* holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(object, holder); JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_ic_state, cache_holder, in_loop, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler( argc, in_loop, kind, extra_ic_state, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallField(JSObject::cast(object), holder, index, name); if (!maybe_code->ToObject(&code)) return maybe_code; } ASSERT_EQ(flags, Code::cast(code)->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = map_holder->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeCallInterceptor( int argc, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, Object* object, JSObject* holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(object, holder); JSObject* map_holder = IC::GetCodeCacheHolder(object, cache_holder); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_ic_state, cache_holder, NOT_IN_LOOP, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler( argc, NOT_IN_LOOP, kind, extra_ic_state, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallInterceptor(JSObject::cast(object), holder, name); if (!maybe_code->ToObject(&code)) return maybe_code; } ASSERT_EQ(flags, Code::cast(code)->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = map_holder->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } MaybeObject* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, JSObject* receiver) { Object* code; { MaybeObject* maybe_code = ComputeCallNormal(argc, in_loop, kind, extra_ic_state); if (!maybe_code->ToObject(&code)) return maybe_code; } return code; } MaybeObject* StubCache::ComputeCallGlobal(int argc, InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, JSObject* receiver, GlobalObject* holder, JSGlobalPropertyCell* cell, JSFunction* function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(receiver, holder); JSObject* map_holder = IC::GetCodeCacheHolder(receiver, cache_holder); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_ic_state, cache_holder, in_loop, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { // If the function hasn't been compiled yet, we cannot do it now // because it may cause GC. To avoid this issue, we return an // internal error which will make sure we do not update any // caches. if (!function->is_compiled()) return Failure::InternalError(); CallStubCompiler compiler( argc, in_loop, kind, extra_ic_state, cache_holder); { MaybeObject* maybe_code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); if (!maybe_code->ToObject(&code)) return maybe_code; } ASSERT_EQ(flags, Code::cast(code)->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), Code::cast(code), name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, name, Code::cast(code))); Object* result; { MaybeObject* maybe_result = map_holder->UpdateMapCodeCache(name, Code::cast(code)); if (!maybe_result->ToObject(&result)) return maybe_result; } } return code; } static Object* GetProbeValue(Isolate* isolate, Code::Flags flags) { // Use raw_unchecked... so we don't get assert failures during GC. NumberDictionary* dictionary = isolate->heap()->raw_unchecked_non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate, flags); if (entry != -1) return dictionary->ValueAt(entry); return isolate->heap()->raw_unchecked_undefined_value(); } MUST_USE_RESULT static MaybeObject* ProbeCache(Isolate* isolate, Code::Flags flags) { Heap* heap = isolate->heap(); Object* probe = GetProbeValue(isolate, flags); if (probe != heap->undefined_value()) return probe; // Seed the cache with an undefined value to make sure that any // generated code object can always be inserted into the cache // without causing allocation failures. Object* result; { MaybeObject* maybe_result = heap->non_monomorphic_cache()->AtNumberPut(flags, heap->undefined_value()); if (!maybe_result->ToObject(&result)) return maybe_result; } heap->public_set_non_monomorphic_cache(NumberDictionary::cast(result)); return probe; } static MaybeObject* FillCache(Isolate* isolate, MaybeObject* maybe_code) { Object* code; if (maybe_code->ToObject(&code)) { if (code->IsCode()) { Heap* heap = isolate->heap(); int entry = heap->non_monomorphic_cache()->FindEntry( Code::cast(code)->flags()); // The entry must be present see comment in ProbeCache. ASSERT(entry != -1); ASSERT(heap->non_monomorphic_cache()->ValueAt(entry) == heap->undefined_value()); heap->non_monomorphic_cache()->ValueAtPut(entry, code); CHECK(GetProbeValue(isolate, Code::cast(code)->flags()) == code); } } return maybe_code; } Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop, RelocInfo::Mode mode, Code::Kind kind) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, in_loop, UNINITIALIZED, extra_state, NORMAL, argc); Object* result = ProbeCache(isolate(), flags)->ToObjectUnchecked(); ASSERT(result != heap()->undefined_value()); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(result);
}
MaybeObject* StubCache::ComputeCallInitialize(int argc,
InLoopFlag in_loop,
RelocInfo::Mode mode,
Code::Kind kind) {
Code::ExtraICState extra_state =
CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) |
CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT);
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
UNINITIALIZED,
extra_state,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallInitialize(flags));
}
Handle StubCache::ComputeCallInitialize(int argc,
InLoopFlag in_loop,
RelocInfo::Mode mode) {
if (in_loop == IN_LOOP) {
// Force the creation of the corresponding stub outside loops,
// because it may be used when clearing the ICs later - it is
// possible for a series of IC transitions to lose the in-loop
// information, and the IC clearing code can't generate a stub
// that it needs so we need to ensure it is generated already.
ComputeCallInitialize(argc, NOT_IN_LOOP, mode);
}
CALL_HEAP_FUNCTION(isolate_,
ComputeCallInitialize(argc, in_loop, mode, Code::CALL_IC),
Code);
}
Handle StubCache::ComputeKeyedCallInitialize(int argc,
InLoopFlag in_loop) {
if (in_loop == IN_LOOP) {
// Force the creation of the corresponding stub outside loops,
// because it may be used when clearing the ICs later - it is
// possible for a series of IC transitions to lose the in-loop
// information, and the IC clearing code can't generate a stub
// that it needs so we need to ensure it is generated already.
ComputeKeyedCallInitialize(argc, NOT_IN_LOOP);
}
CALL_HEAP_FUNCTION(
isolate_,
ComputeCallInitialize(argc,
in_loop,
RelocInfo::CODE_TARGET,
Code::KEYED_CALL_IC),
Code);
}
MaybeObject* StubCache::ComputeCallPreMonomorphic(
int argc,
InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
PREMONOMORPHIC,
extra_ic_state,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallPreMonomorphic(flags));
}
MaybeObject* StubCache::ComputeCallNormal(int argc,
InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
MONOMORPHIC,
extra_ic_state,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallNormal(flags));
}
MaybeObject* StubCache::ComputeCallArguments(int argc,
InLoopFlag in_loop,
Code::Kind kind) {
ASSERT(kind == Code::KEYED_CALL_IC);
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
MEGAMORPHIC,
Code::kNoExtraICState,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallArguments(flags));
}
MaybeObject* StubCache::ComputeCallMegamorphic(
int argc,
InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
in_loop,
MEGAMORPHIC,
extra_ic_state,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallMegamorphic(flags));
}
MaybeObject* StubCache::ComputeCallMiss(int argc,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
// MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs
// and monomorphic stubs are not mixed up together in the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
MONOMORPHIC_PROTOTYPE_FAILURE,
extra_ic_state,
NORMAL,
argc,
OWN_MAP);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallMiss(flags));
}
#ifdef ENABLE_DEBUGGER_SUPPORT
MaybeObject* StubCache::ComputeCallDebugBreak(
int argc,
Code::Kind kind) {
// Extra IC state is irrelevant for debug break ICs. They jump to
// the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
DEBUG_BREAK,
Code::kNoExtraICState,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallDebugBreak(flags));
}
MaybeObject* StubCache::ComputeCallDebugPrepareStepIn(
int argc,
Code::Kind kind) {
// Extra IC state is irrelevant for debug break ICs. They jump to
// the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
NOT_IN_LOOP,
DEBUG_PREPARE_STEP_IN,
Code::kNoExtraICState,
NORMAL,
argc);
Object* probe;
{ MaybeObject* maybe_probe = ProbeCache(isolate_, flags);
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
}
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(isolate_, compiler.CompileCallDebugPrepareStepIn(flags));
}
#endif
void StubCache::Clear() {
for (int i = 0; i < kPrimaryTableSize; i++) {
primary_[i].key = heap()->empty_string();
primary_[i].value = isolate_->builtins()->builtin(
Builtins::kIllegal);
}
for (int j = 0; j < kSecondaryTableSize; j++) {
secondary_[j].key = heap()->empty_string();
secondary_[j].value = isolate_->builtins()->builtin(
Builtins::kIllegal);
}
}
void StubCache::CollectMatchingMaps(SmallMapList* types,
String* name,
Code::Flags flags) {
for (int i = 0; i < kPrimaryTableSize; i++) {
if (primary_[i].key == name) {
Map* map = primary_[i].value->FindFirstMap();
// Map can be NULL, if the stub is constant function call
// with a primitive receiver.
if (map == NULL) continue;
int offset = PrimaryOffset(name, flags, map);
if (entry(primary_, offset) == &primary_[i]) {
types->Add(Handle