// 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 "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);
ASSERT(Code::kFlagsICStateShift == 0);
// 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;
}
namespace {
ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) {
switch (kind) {
case JSObject::EXTERNAL_BYTE_ELEMENTS:
return kExternalByteArray;
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
return kExternalUnsignedByteArray;
case JSObject::EXTERNAL_SHORT_ELEMENTS:
return kExternalShortArray;
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
return kExternalUnsignedShortArray;
case JSObject::EXTERNAL_INT_ELEMENTS:
return kExternalIntArray;
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
return kExternalUnsignedIntArray;
case JSObject::EXTERNAL_FLOAT_ELEMENTS:
return kExternalFloatArray;
case JSObject::EXTERNAL_DOUBLE_ELEMENTS:
return kExternalDoubleArray;
case JSObject::EXTERNAL_PIXEL_ELEMENTS:
return kExternalPixelArray;
default:
UNREACHABLE();
return static_cast(0);
}
}
} // anonymous namespace
MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray(
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);
ExternalArrayType array_type =
ElementsKindToExternalArrayType(receiver->GetElementsKind());
String* name = is_store
? isolate()->heap()->KeyedStoreSpecializedMonomorphic_symbol()
: isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
Object* maybe_code = receiver->map()->FindInCodeCache(name, flags);
if (!maybe_code->IsUndefined()) return Code::cast(maybe_code);
MaybeObject* maybe_new_code = NULL;
if (is_store) {
ExternalArrayStoreStubCompiler compiler(strict_mode);
maybe_new_code = compiler.CompileStore(receiver, array_type);
} else {
ExternalArrayLoadStubCompiler compiler(strict_mode);
maybe_new_code = compiler.CompileLoad(receiver, array_type);
}
Code* code;
if (!maybe_new_code->To(&code)) return maybe_new_code;
code->set_external_array_type(array_type);
if (is_store) {
PROFILE(isolate_,
CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_STORE_IC_TAG,
Code::cast(code), 0));
} else {
PROFILE(isolate_,
CodeCreateEvent(Logger::KEYED_EXTERNAL_ARRAY_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::ComputeKeyedLoadOrStoreFastElement(
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()->KeyedStoreSpecializedMonomorphic_symbol()
: isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
Object* maybe_code = receiver->map()->FindInCodeCache(name, flags);
if (!maybe_code->IsUndefined()) return Code::cast(maybe_code);
MaybeObject* maybe_new_code = NULL;
if (is_store) {
KeyedStoreStubCompiler compiler(strict_mode);
maybe_new_code = compiler.CompileStoreFastElement(receiver->map());
} else {
KeyedLoadStubCompiler compiler;
maybe_new_code = compiler.CompileLoadFastElement(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::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(ZoneMapList* 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