Introduce extra IC state to record additional feedback from IC-s.
Extra IC state is only two bits and only supported for call IC-s for now. To change its extra state an IC stub jumps to a new miss stub that goes to runtime as usual but then instead of going megamorphic generates a monomorphic stub with the updated state. Review URL: http://codereview.chromium.org/6344005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6370 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
b4c88d5de4
commit
11a4cb5718
@ -542,8 +542,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
Label number, non_number, non_string, boolean, probe, miss;
|
||||
|
||||
// Probe the stub cache.
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
MONOMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5);
|
||||
|
||||
// If the stub cache probing failed, the receiver might be a value.
|
||||
|
@ -1332,11 +1332,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
|
||||
|
||||
|
||||
MaybeObject* CallStubCompiler::GenerateMissBranch() {
|
||||
MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(),
|
||||
kind_);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj =
|
||||
StubCache::ComputeCallMiss(arguments().immediate(), kind_);
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
}
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
__ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
|
||||
return obj;
|
||||
}
|
||||
@ -1646,8 +1645,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
GenerateNameCheck(name, &miss);
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1675,7 +1681,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_code_at_generator.GenerateFast(masm());
|
||||
__ Drop(argc + 1);
|
||||
@ -1684,12 +1690,17 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_code_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(r0, Heap::kNanValueRootIndex);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(r0, Heap::kNanValueRootIndex);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in r2.
|
||||
__ Move(r2, Handle<String>(name));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
@ -1720,9 +1731,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1752,7 +1769,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_at_generator.GenerateFast(masm());
|
||||
__ Drop(argc + 1);
|
||||
@ -1761,12 +1778,17 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(r0, Heap::kEmptyStringRootIndex);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(r0, Heap::kEmptyStringRootIndex);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in r2.
|
||||
__ Move(r2, Handle<String>(name));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
|
@ -1231,8 +1231,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
Label number, non_number, non_string, boolean, probe, miss;
|
||||
|
||||
// Probe the stub cache.
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
MONOMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, eax);
|
||||
|
||||
// If the stub cache probing failed, the receiver might be a value.
|
||||
@ -1325,7 +1329,9 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
|
||||
}
|
||||
|
||||
|
||||
static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
static void GenerateCallMiss(MacroAssembler* masm,
|
||||
int argc,
|
||||
IC::UtilityId id) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
|
@ -1362,11 +1362,10 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
|
||||
|
||||
|
||||
MaybeObject* CallStubCompiler::GenerateMissBranch() {
|
||||
MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(),
|
||||
kind_);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj =
|
||||
StubCache::ComputeCallMiss(arguments().immediate(), kind_);
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
}
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
__ jmp(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
|
||||
return obj;
|
||||
}
|
||||
@ -1685,9 +1684,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1715,7 +1720,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_code_at_generator.GenerateFast(masm());
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
@ -1723,11 +1728,16 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_code_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ Set(eax, Immediate(Factory::nan_value()));
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ Set(eax, Immediate(Factory::nan_value()));
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in ecx.
|
||||
__ Set(ecx, Immediate(Handle<String>(name)));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
@ -1758,9 +1768,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1790,7 +1806,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_at_generator.GenerateFast(masm());
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
@ -1798,11 +1814,16 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ Set(eax, Immediate(Factory::empty_string()));
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ Set(eax, Immediate(Factory::empty_string()));
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in ecx.
|
||||
__ Set(ecx, Immediate(Handle<String>(name)));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
|
309
src/ic.cc
309
src/ic.cc
@ -154,24 +154,20 @@ static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup,
|
||||
}
|
||||
|
||||
|
||||
IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
|
||||
IC::State state = target->ic_state();
|
||||
|
||||
if (state != MONOMORPHIC || !name->IsString()) return state;
|
||||
if (receiver->IsUndefined() || receiver->IsNull()) return state;
|
||||
|
||||
static bool TryRemoveInvalidPrototypeDependentStub(Code* target,
|
||||
Object* receiver,
|
||||
Object* name) {
|
||||
InlineCacheHolderFlag cache_holder =
|
||||
Code::ExtractCacheHolderFromFlags(target->flags());
|
||||
|
||||
|
||||
if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
|
||||
// The stub was generated for JSObject but called for non-JSObject.
|
||||
// IC::GetCodeCacheHolder is not applicable.
|
||||
return MONOMORPHIC;
|
||||
return false;
|
||||
} else if (cache_holder == PROTOTYPE_MAP &&
|
||||
receiver->GetPrototype()->IsNull()) {
|
||||
// IC::GetCodeCacheHolder is not applicable.
|
||||
return MONOMORPHIC;
|
||||
return false;
|
||||
}
|
||||
Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map();
|
||||
|
||||
@ -185,20 +181,37 @@ IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
|
||||
// to prototype check failure.
|
||||
int index = map->IndexInCodeCache(name, target);
|
||||
if (index >= 0) {
|
||||
// For keyed load/store/call, the most likely cause of cache failure is
|
||||
// that the key has changed. We do not distinguish between
|
||||
// prototype and non-prototype failures for keyed access.
|
||||
Code::Kind kind = target->kind();
|
||||
if (kind == Code::KEYED_LOAD_IC ||
|
||||
kind == Code::KEYED_STORE_IC ||
|
||||
kind == Code::KEYED_CALL_IC) {
|
||||
return MONOMORPHIC;
|
||||
}
|
||||
|
||||
// Remove the target from the code cache to avoid hitting the same
|
||||
// invalid stub again.
|
||||
map->RemoveFromCodeCache(String::cast(name), target, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
|
||||
IC::State state = target->ic_state();
|
||||
|
||||
if (state != MONOMORPHIC || !name->IsString()) return state;
|
||||
if (receiver->IsUndefined() || receiver->IsNull()) return state;
|
||||
|
||||
// For keyed load/store/call, the most likely cause of cache failure is
|
||||
// that the key has changed. We do not distinguish between
|
||||
// prototype and non-prototype failures for keyed access.
|
||||
Code::Kind kind = target->kind();
|
||||
if (kind == Code::KEYED_LOAD_IC ||
|
||||
kind == Code::KEYED_STORE_IC ||
|
||||
kind == Code::KEYED_CALL_IC) {
|
||||
return MONOMORPHIC;
|
||||
}
|
||||
|
||||
// Remove the target from the code cache if it became invalid
|
||||
// because of changes in the prototype chain to avoid hitting it
|
||||
// again.
|
||||
// Call stubs handle this later to allow extra IC state
|
||||
// transitions.
|
||||
if (kind != Code::CALL_IC &&
|
||||
TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) {
|
||||
return MONOMORPHIC_PROTOTYPE_FAILURE;
|
||||
}
|
||||
|
||||
@ -482,6 +495,7 @@ void CallICBase::ReceiverToObject(Handle<Object> object) {
|
||||
|
||||
|
||||
MaybeObject* CallICBase::LoadFunction(State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
// If the object is undefined or null it's illegal to try to get any
|
||||
@ -527,7 +541,7 @@ MaybeObject* CallICBase::LoadFunction(State state,
|
||||
|
||||
// Lookup is valid: Update inline cache and stub cache.
|
||||
if (FLAG_use_ic) {
|
||||
UpdateCaches(&lookup, state, object, name);
|
||||
UpdateCaches(&lookup, state, extra_ic_state, object, name);
|
||||
}
|
||||
|
||||
// Get the property.
|
||||
@ -576,8 +590,142 @@ MaybeObject* CallICBase::LoadFunction(State state,
|
||||
}
|
||||
|
||||
|
||||
bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
|
||||
Handle<Object> object,
|
||||
Code::ExtraICState* extra_ic_state) {
|
||||
ASSERT(kind_ == Code::CALL_IC);
|
||||
if (lookup->type() != CONSTANT_FUNCTION) return false;
|
||||
JSFunction* function = lookup->GetConstantFunction();
|
||||
if (!function->shared()->HasBuiltinFunctionId()) return false;
|
||||
|
||||
// Fetch the arguments passed to the called function.
|
||||
const int argc = target()->arguments_count();
|
||||
Address entry = Top::c_entry_fp(Top::GetCurrentThread());
|
||||
Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
|
||||
Arguments args(argc + 1,
|
||||
&Memory::Object_at(fp +
|
||||
StandardFrameConstants::kCallerSPOffset +
|
||||
argc * kPointerSize));
|
||||
switch (function->shared()->builtin_function_id()) {
|
||||
case kStringCharCodeAt:
|
||||
case kStringCharAt:
|
||||
if (object->IsString()) {
|
||||
String* string = String::cast(*object);
|
||||
// Check that there's the right wrapper in the receiver slot.
|
||||
ASSERT(string == JSValue::cast(args[0])->value());
|
||||
// If we're in the default (fastest) state and the index is
|
||||
// out of bounds, update the state to record this fact.
|
||||
if (*extra_ic_state == DEFAULT_STRING_STUB &&
|
||||
argc >= 1 && args[1]->IsNumber()) {
|
||||
double index;
|
||||
if (args[1]->IsSmi()) {
|
||||
index = Smi::cast(args[1])->value();
|
||||
} else {
|
||||
ASSERT(args[1]->IsHeapNumber());
|
||||
index = DoubleToInteger(HeapNumber::cast(args[1])->value());
|
||||
}
|
||||
if (index < 0 || index >= string->length()) {
|
||||
*extra_ic_state = STRING_INDEX_OUT_OF_BOUNDS;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
MaybeObject* CallICBase::ComputeMonomorphicStub(
|
||||
LookupResult* lookup,
|
||||
State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
int argc = target()->arguments_count();
|
||||
InLoopFlag in_loop = target()->ic_in_loop();
|
||||
MaybeObject* maybe_code = NULL;
|
||||
switch (lookup->type()) {
|
||||
case FIELD: {
|
||||
int index = lookup->GetFieldIndex();
|
||||
maybe_code = StubCache::ComputeCallField(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
index);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_FUNCTION: {
|
||||
// Get the constant function and compute the code stub for this
|
||||
// call; used for rewriting to monomorphic state and making sure
|
||||
// that the code stub is in the stub cache.
|
||||
JSFunction* function = lookup->GetConstantFunction();
|
||||
maybe_code = StubCache::ComputeCallConstant(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
extra_ic_state,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
function);
|
||||
break;
|
||||
}
|
||||
case NORMAL: {
|
||||
if (!object->IsJSObject()) return NULL;
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||
|
||||
if (lookup->holder()->IsGlobalObject()) {
|
||||
GlobalObject* global = GlobalObject::cast(lookup->holder());
|
||||
JSGlobalPropertyCell* cell =
|
||||
JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
|
||||
if (!cell->value()->IsJSFunction()) return NULL;
|
||||
JSFunction* function = JSFunction::cast(cell->value());
|
||||
maybe_code = StubCache::ComputeCallGlobal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver,
|
||||
global,
|
||||
cell,
|
||||
function);
|
||||
} else {
|
||||
// There is only one shared stub for calling normalized
|
||||
// properties. It does not traverse the prototype chain, so the
|
||||
// property must be found in the receiver for the stub to be
|
||||
// applicable.
|
||||
if (lookup->holder() != *receiver) return NULL;
|
||||
maybe_code = StubCache::ComputeCallNormal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INTERCEPTOR: {
|
||||
ASSERT(HasInterceptorGetter(lookup->holder()));
|
||||
maybe_code = StubCache::ComputeCallInterceptor(argc,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
maybe_code = NULL;
|
||||
break;
|
||||
}
|
||||
return maybe_code;
|
||||
}
|
||||
|
||||
|
||||
void CallICBase::UpdateCaches(LookupResult* lookup,
|
||||
State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
// Bail out if we didn't find a result.
|
||||
@ -594,90 +742,44 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
|
||||
int argc = target()->arguments_count();
|
||||
InLoopFlag in_loop = target()->ic_in_loop();
|
||||
MaybeObject* maybe_code = NULL;
|
||||
Object* code;
|
||||
bool had_proto_failure = false;
|
||||
if (state == UNINITIALIZED) {
|
||||
// This is the first time we execute this inline cache.
|
||||
// Set the target to the pre monomorphic stub to delay
|
||||
// setting the monomorphic state.
|
||||
maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
|
||||
} else if (state == MONOMORPHIC) {
|
||||
maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
|
||||
} else {
|
||||
// Compute monomorphic stub.
|
||||
switch (lookup->type()) {
|
||||
case FIELD: {
|
||||
int index = lookup->GetFieldIndex();
|
||||
maybe_code = StubCache::ComputeCallField(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
index);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_FUNCTION: {
|
||||
// Get the constant function and compute the code stub for this
|
||||
// call; used for rewriting to monomorphic state and making sure
|
||||
// that the code stub is in the stub cache.
|
||||
JSFunction* function = lookup->GetConstantFunction();
|
||||
maybe_code = StubCache::ComputeCallConstant(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
function);
|
||||
break;
|
||||
}
|
||||
case NORMAL: {
|
||||
if (!object->IsJSObject()) return;
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||
|
||||
if (lookup->holder()->IsGlobalObject()) {
|
||||
GlobalObject* global = GlobalObject::cast(lookup->holder());
|
||||
JSGlobalPropertyCell* cell =
|
||||
JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
|
||||
if (!cell->value()->IsJSFunction()) return;
|
||||
JSFunction* function = JSFunction::cast(cell->value());
|
||||
maybe_code = StubCache::ComputeCallGlobal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver,
|
||||
global,
|
||||
cell,
|
||||
function);
|
||||
} else {
|
||||
// There is only one shared stub for calling normalized
|
||||
// properties. It does not traverse the prototype chain, so the
|
||||
// property must be found in the receiver for the stub to be
|
||||
// applicable.
|
||||
if (lookup->holder() != *receiver) return;
|
||||
maybe_code = StubCache::ComputeCallNormal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INTERCEPTOR: {
|
||||
ASSERT(HasInterceptorGetter(lookup->holder()));
|
||||
maybe_code = StubCache::ComputeCallInterceptor(argc,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
if (kind_ == Code::CALL_IC &&
|
||||
TryUpdateExtraICState(lookup, object, &extra_ic_state)) {
|
||||
maybe_code = ComputeMonomorphicStub(lookup,
|
||||
state,
|
||||
extra_ic_state,
|
||||
object,
|
||||
name);
|
||||
} else if (kind_ == Code::CALL_IC &&
|
||||
TryRemoveInvalidPrototypeDependentStub(target(),
|
||||
*object,
|
||||
*name)) {
|
||||
had_proto_failure = true;
|
||||
maybe_code = ComputeMonomorphicStub(lookup,
|
||||
state,
|
||||
extra_ic_state,
|
||||
object,
|
||||
name);
|
||||
} else {
|
||||
maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
|
||||
}
|
||||
} else {
|
||||
maybe_code = ComputeMonomorphicStub(lookup,
|
||||
state,
|
||||
extra_ic_state,
|
||||
object,
|
||||
name);
|
||||
}
|
||||
|
||||
// If we're unable to compute the stub (not enough memory left), we
|
||||
// simply avoid updating the caches.
|
||||
Object* code;
|
||||
if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
|
||||
|
||||
// Patch the call site depending on the state of the cache.
|
||||
@ -696,7 +798,9 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
|
||||
StubCache::Set(*name, map, Code::cast(code));
|
||||
}
|
||||
|
||||
USE(had_proto_failure);
|
||||
#ifdef DEBUG
|
||||
if (had_proto_failure) state = MONOMORPHIC_PROTOTYPE_FAILURE;
|
||||
TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
|
||||
name, state, target(), in_loop ? " (in-loop)" : "");
|
||||
#endif
|
||||
@ -707,7 +811,10 @@ MaybeObject* KeyedCallIC::LoadFunction(State state,
|
||||
Handle<Object> object,
|
||||
Handle<Object> key) {
|
||||
if (key->IsSymbol()) {
|
||||
return CallICBase::LoadFunction(state, object, Handle<String>::cast(key));
|
||||
return CallICBase::LoadFunction(state,
|
||||
Code::kNoExtraICState,
|
||||
object,
|
||||
Handle<String>::cast(key));
|
||||
}
|
||||
|
||||
if (object->IsUndefined() || object->IsNull()) {
|
||||
@ -1641,11 +1748,13 @@ MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) {
|
||||
ASSERT(args.length() == 2);
|
||||
CallIC ic;
|
||||
IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
|
||||
Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
|
||||
MaybeObject* maybe_result = ic.LoadFunction(state,
|
||||
extra_ic_state,
|
||||
args.at<Object>(0),
|
||||
args.at<String>(1));
|
||||
Object* result;
|
||||
{ MaybeObject* maybe_result =
|
||||
ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
|
||||
if (!maybe_result->ToObject(&result)) return maybe_result;
|
||||
}
|
||||
if (!maybe_result->ToObject(&result)) return maybe_result;
|
||||
|
||||
// The first time the inline cache is updated may be the first time the
|
||||
// function it references gets called. If the function was lazily compiled
|
||||
|
13
src/ic.h
13
src/ic.h
@ -193,16 +193,29 @@ class CallICBase: public IC {
|
||||
|
||||
public:
|
||||
MUST_USE_RESULT MaybeObject* LoadFunction(State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name);
|
||||
|
||||
protected:
|
||||
Code::Kind kind_;
|
||||
|
||||
bool TryUpdateExtraICState(LookupResult* lookup,
|
||||
Handle<Object> object,
|
||||
Code::ExtraICState* extra_ic_state);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* ComputeMonomorphicStub(
|
||||
LookupResult* lookup,
|
||||
State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name);
|
||||
|
||||
// Update the inline cache and the global stub cache based on the
|
||||
// lookup result.
|
||||
void UpdateCaches(LookupResult* lookup,
|
||||
State state,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name);
|
||||
|
||||
|
@ -2416,6 +2416,12 @@ InlineCacheState Code::ic_state() {
|
||||
}
|
||||
|
||||
|
||||
Code::ExtraICState Code::extra_ic_state() {
|
||||
ASSERT(is_inline_cache_stub());
|
||||
return ExtractExtraICStateFromFlags(flags());
|
||||
}
|
||||
|
||||
|
||||
PropertyType Code::type() {
|
||||
ASSERT(ic_state() == MONOMORPHIC);
|
||||
return ExtractTypeFromFlags(flags());
|
||||
@ -2592,14 +2598,20 @@ bool Code::is_inline_cache_stub() {
|
||||
Code::Flags Code::ComputeFlags(Kind kind,
|
||||
InLoopFlag in_loop,
|
||||
InlineCacheState ic_state,
|
||||
ExtraICState extra_ic_state,
|
||||
PropertyType type,
|
||||
int argc,
|
||||
InlineCacheHolderFlag holder) {
|
||||
// Extra IC state is only allowed for monomorphic call IC stubs.
|
||||
ASSERT(extra_ic_state == kNoExtraICState ||
|
||||
(kind == CALL_IC && (ic_state == MONOMORPHIC ||
|
||||
ic_state == MONOMORPHIC_PROTOTYPE_FAILURE)));
|
||||
// Compute the bit mask.
|
||||
int bits = kind << kFlagsKindShift;
|
||||
if (in_loop) bits |= kFlagsICInLoopMask;
|
||||
bits |= ic_state << kFlagsICStateShift;
|
||||
bits |= type << kFlagsTypeShift;
|
||||
bits |= extra_ic_state << kFlagsExtraICStateShift;
|
||||
bits |= argc << kFlagsArgumentsCountShift;
|
||||
if (holder == PROTOTYPE_MAP) bits |= kFlagsCacheInPrototypeMapMask;
|
||||
// Cast to flags and validate result before returning it.
|
||||
@ -2608,6 +2620,7 @@ Code::Flags Code::ComputeFlags(Kind kind,
|
||||
ASSERT(ExtractICStateFromFlags(result) == ic_state);
|
||||
ASSERT(ExtractICInLoopFromFlags(result) == in_loop);
|
||||
ASSERT(ExtractTypeFromFlags(result) == type);
|
||||
ASSERT(ExtractExtraICStateFromFlags(result) == extra_ic_state);
|
||||
ASSERT(ExtractArgumentsCountFromFlags(result) == argc);
|
||||
return result;
|
||||
}
|
||||
@ -2615,10 +2628,12 @@ Code::Flags Code::ComputeFlags(Kind kind,
|
||||
|
||||
Code::Flags Code::ComputeMonomorphicFlags(Kind kind,
|
||||
PropertyType type,
|
||||
ExtraICState extra_ic_state,
|
||||
InlineCacheHolderFlag holder,
|
||||
InLoopFlag in_loop,
|
||||
int argc) {
|
||||
return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc, holder);
|
||||
return ComputeFlags(
|
||||
kind, in_loop, MONOMORPHIC, extra_ic_state, type, argc, holder);
|
||||
}
|
||||
|
||||
|
||||
@ -2634,6 +2649,12 @@ InlineCacheState Code::ExtractICStateFromFlags(Flags flags) {
|
||||
}
|
||||
|
||||
|
||||
Code::ExtraICState Code::ExtractExtraICStateFromFlags(Flags flags) {
|
||||
int bits = (flags & kFlagsExtraICStateMask) >> kFlagsExtraICStateShift;
|
||||
return static_cast<ExtraICState>(bits);
|
||||
}
|
||||
|
||||
|
||||
InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) {
|
||||
int bits = (flags & kFlagsICInLoopMask);
|
||||
return bits != 0 ? IN_LOOP : NOT_IN_LOOP;
|
||||
|
@ -3168,6 +3168,10 @@ class Code: public HeapObject {
|
||||
NUMBER_OF_KINDS = LAST_IC_KIND + 1
|
||||
};
|
||||
|
||||
typedef int ExtraICState;
|
||||
|
||||
static const ExtraICState kNoExtraICState = 0;
|
||||
|
||||
#ifdef ENABLE_DISASSEMBLER
|
||||
// Printing
|
||||
static const char* Kind2String(Kind kind);
|
||||
@ -3203,6 +3207,7 @@ class Code: public HeapObject {
|
||||
// [flags]: Access to specific code flags.
|
||||
inline Kind kind();
|
||||
inline InlineCacheState ic_state(); // Only valid for IC stubs.
|
||||
inline ExtraICState extra_ic_state(); // Only valid for IC stubs.
|
||||
inline InLoopFlag ic_in_loop(); // Only valid for IC stubs.
|
||||
inline PropertyType type(); // Only valid for monomorphic IC stubs.
|
||||
inline int arguments_count(); // Only valid for call IC stubs.
|
||||
@ -3287,22 +3292,26 @@ class Code: public HeapObject {
|
||||
Map* FindFirstMap();
|
||||
|
||||
// Flags operations.
|
||||
static inline Flags ComputeFlags(Kind kind,
|
||||
InLoopFlag in_loop = NOT_IN_LOOP,
|
||||
InlineCacheState ic_state = UNINITIALIZED,
|
||||
PropertyType type = NORMAL,
|
||||
int argc = -1,
|
||||
InlineCacheHolderFlag holder = OWN_MAP);
|
||||
static inline Flags ComputeFlags(
|
||||
Kind kind,
|
||||
InLoopFlag in_loop = NOT_IN_LOOP,
|
||||
InlineCacheState ic_state = UNINITIALIZED,
|
||||
ExtraICState extra_ic_state = kNoExtraICState,
|
||||
PropertyType type = NORMAL,
|
||||
int argc = -1,
|
||||
InlineCacheHolderFlag holder = OWN_MAP);
|
||||
|
||||
static inline Flags ComputeMonomorphicFlags(
|
||||
Kind kind,
|
||||
PropertyType type,
|
||||
ExtraICState extra_ic_state = kNoExtraICState,
|
||||
InlineCacheHolderFlag holder = OWN_MAP,
|
||||
InLoopFlag in_loop = NOT_IN_LOOP,
|
||||
int argc = -1);
|
||||
|
||||
static inline Kind ExtractKindFromFlags(Flags flags);
|
||||
static inline InlineCacheState ExtractICStateFromFlags(Flags flags);
|
||||
static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags);
|
||||
static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags);
|
||||
static inline PropertyType ExtractTypeFromFlags(Flags flags);
|
||||
static inline int ExtractArgumentsCountFromFlags(Flags flags);
|
||||
@ -3423,14 +3432,16 @@ class Code: public HeapObject {
|
||||
static const int kFlagsTypeShift = 4;
|
||||
static const int kFlagsKindShift = 7;
|
||||
static const int kFlagsICHolderShift = 11;
|
||||
static const int kFlagsArgumentsCountShift = 12;
|
||||
static const int kFlagsExtraICStateShift = 12;
|
||||
static const int kFlagsArgumentsCountShift = 14;
|
||||
|
||||
static const int kFlagsICStateMask = 0x00000007; // 00000000111
|
||||
static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
|
||||
static const int kFlagsTypeMask = 0x00000070; // 00001110000
|
||||
static const int kFlagsKindMask = 0x00000780; // 11110000000
|
||||
static const int kFlagsCacheInPrototypeMapMask = 0x00000800;
|
||||
static const int kFlagsArgumentsCountMask = 0xFFFFF000;
|
||||
static const int kFlagsExtraICStateMask = 0x00003000;
|
||||
static const int kFlagsArgumentsCountMask = 0xFFFFC000;
|
||||
|
||||
static const int kFlagsNotUsedInLookup =
|
||||
(kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask);
|
||||
|
@ -613,6 +613,7 @@ MaybeObject* StubCache::ComputeKeyedStoreField(String* name,
|
||||
MaybeObject* StubCache::ComputeCallConstant(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
@ -632,12 +633,12 @@ MaybeObject* StubCache::ComputeCallConstant(int argc,
|
||||
check = BOOLEAN_CHECK;
|
||||
}
|
||||
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
CONSTANT_FUNCTION,
|
||||
cache_holder,
|
||||
in_loop,
|
||||
argc);
|
||||
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
|
||||
@ -646,7 +647,8 @@ MaybeObject* StubCache::ComputeCallConstant(int argc,
|
||||
// caches.
|
||||
if (!function->is_compiled()) return Failure::InternalError();
|
||||
// Compile the stub - only create stubs for fully compiled functions.
|
||||
CallStubCompiler compiler(argc, in_loop, kind, cache_holder);
|
||||
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;
|
||||
@ -687,12 +689,14 @@ MaybeObject* StubCache::ComputeCallField(int argc,
|
||||
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
|
||||
FIELD,
|
||||
Code::kNoExtraICState,
|
||||
cache_holder,
|
||||
in_loop,
|
||||
argc);
|
||||
Object* code = map_holder->map()->FindInCodeCache(name, flags);
|
||||
if (code->IsUndefined()) {
|
||||
CallStubCompiler compiler(argc, in_loop, kind, cache_holder);
|
||||
CallStubCompiler compiler(
|
||||
argc, in_loop, kind, Code::kNoExtraICState, cache_holder);
|
||||
{ MaybeObject* maybe_code =
|
||||
compiler.CompileCallField(JSObject::cast(object),
|
||||
holder,
|
||||
@ -731,15 +735,16 @@ MaybeObject* StubCache::ComputeCallInterceptor(int argc,
|
||||
object = holder;
|
||||
}
|
||||
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
INTERCEPTOR,
|
||||
cache_holder,
|
||||
NOT_IN_LOOP,
|
||||
argc);
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
|
||||
INTERCEPTOR,
|
||||
Code::kNoExtraICState,
|
||||
cache_holder,
|
||||
NOT_IN_LOOP,
|
||||
argc);
|
||||
Object* code = map_holder->map()->FindInCodeCache(name, flags);
|
||||
if (code->IsUndefined()) {
|
||||
CallStubCompiler compiler(argc, NOT_IN_LOOP, kind, cache_holder);
|
||||
CallStubCompiler compiler(
|
||||
argc, NOT_IN_LOOP, kind, Code::kNoExtraICState, cache_holder);
|
||||
{ MaybeObject* maybe_code =
|
||||
compiler.CompileCallInterceptor(JSObject::cast(object), holder, name);
|
||||
if (!maybe_code->ToObject(&code)) return maybe_code;
|
||||
@ -782,12 +787,12 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc,
|
||||
InlineCacheHolderFlag cache_holder =
|
||||
IC::GetCodeCacheForObject(receiver, holder);
|
||||
JSObject* map_holder = IC::GetCodeCacheHolder(receiver, cache_holder);
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
NORMAL,
|
||||
cache_holder,
|
||||
in_loop,
|
||||
argc);
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
|
||||
NORMAL,
|
||||
Code::kNoExtraICState,
|
||||
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
|
||||
@ -795,7 +800,8 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc,
|
||||
// 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, cache_holder);
|
||||
CallStubCompiler compiler(
|
||||
argc, in_loop, kind, Code::kNoExtraICState, cache_holder);
|
||||
{ MaybeObject* maybe_code =
|
||||
compiler.CompileCallGlobal(receiver, holder, cell, function, name);
|
||||
if (!maybe_code->ToObject(&code)) return maybe_code;
|
||||
@ -862,8 +868,12 @@ static MaybeObject* FillCache(MaybeObject* maybe_code) {
|
||||
Code* StubCache::FindCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
in_loop,
|
||||
UNINITIALIZED,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* result = ProbeCache(flags)->ToObjectUnchecked();
|
||||
ASSERT(!result->IsUndefined());
|
||||
// This might be called during the marking phase of the collector
|
||||
@ -875,8 +885,12 @@ Code* StubCache::FindCallInitialize(int argc,
|
||||
MaybeObject* StubCache::ComputeCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
in_loop,
|
||||
UNINITIALIZED,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -918,8 +932,12 @@ Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc,
|
||||
MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, in_loop, PREMONOMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
in_loop,
|
||||
PREMONOMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -933,8 +951,12 @@ MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc,
|
||||
MaybeObject* StubCache::ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, in_loop, MONOMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
in_loop,
|
||||
MONOMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -948,8 +970,12 @@ MaybeObject* StubCache::ComputeCallNormal(int argc,
|
||||
MaybeObject* StubCache::ComputeCallMegamorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, in_loop, MEGAMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
in_loop,
|
||||
MEGAMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -963,8 +989,13 @@ MaybeObject* StubCache::ComputeCallMegamorphic(int argc,
|
||||
MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) {
|
||||
// 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, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
MONOMORPHIC_PROTOTYPE_FAILURE,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc,
|
||||
OWN_MAP);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -977,8 +1008,12 @@ MaybeObject* StubCache::ComputeCallMiss(int argc, Code::Kind kind) {
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
DEBUG_BREAK,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -991,12 +1026,12 @@ MaybeObject* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
|
||||
|
||||
MaybeObject* StubCache::ComputeCallDebugPrepareStepIn(int argc,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
DEBUG_PREPARE_STEP_IN,
|
||||
NORMAL,
|
||||
argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
DEBUG_PREPARE_STEP_IN,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
Object* probe;
|
||||
{ MaybeObject* maybe_probe = ProbeCache(flags);
|
||||
if (!maybe_probe->ToObject(&probe)) return maybe_probe;
|
||||
@ -1533,11 +1568,13 @@ MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
|
||||
CallStubCompiler::CallStubCompiler(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
InlineCacheHolderFlag cache_holder)
|
||||
: arguments_(argc)
|
||||
, in_loop_(in_loop)
|
||||
, kind_(kind)
|
||||
, cache_holder_(cache_holder) {
|
||||
: arguments_(argc),
|
||||
in_loop_(in_loop),
|
||||
kind_(kind),
|
||||
extra_ic_state_(extra_ic_state),
|
||||
cache_holder_(cache_holder) {
|
||||
}
|
||||
|
||||
|
||||
@ -1574,6 +1611,7 @@ MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) {
|
||||
int argc = arguments_.immediate();
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind_,
|
||||
type,
|
||||
extra_ic_state_,
|
||||
cache_holder_,
|
||||
in_loop_,
|
||||
argc);
|
||||
|
@ -177,13 +177,15 @@ class StubCache : public AllStatic {
|
||||
JSObject* holder,
|
||||
int index);
|
||||
|
||||
MUST_USE_RESULT static MaybeObject* ComputeCallConstant(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
JSFunction* function);
|
||||
MUST_USE_RESULT static MaybeObject* ComputeCallConstant(
|
||||
int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
JSFunction* function);
|
||||
|
||||
MUST_USE_RESULT static MaybeObject* ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
@ -660,6 +662,7 @@ class CallStubCompiler: public StubCompiler {
|
||||
CallStubCompiler(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
Code::ExtraICState extra_ic_state,
|
||||
InlineCacheHolderFlag cache_holder);
|
||||
|
||||
MUST_USE_RESULT MaybeObject* CompileCallField(JSObject* object,
|
||||
@ -705,6 +708,7 @@ class CallStubCompiler: public StubCompiler {
|
||||
const ParameterCount arguments_;
|
||||
const InLoopFlag in_loop_;
|
||||
const Code::Kind kind_;
|
||||
const Code::ExtraICState extra_ic_state_;
|
||||
const InlineCacheHolderFlag cache_holder_;
|
||||
|
||||
const ParameterCount& arguments() { return arguments_; }
|
||||
|
@ -58,6 +58,9 @@ TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
|
||||
}
|
||||
|
||||
|
||||
STATIC_ASSERT(DEFAULT_STRING_STUB == Code::kNoExtraICState);
|
||||
|
||||
|
||||
TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
|
||||
Handle<Context> global_context) {
|
||||
global_context_ = global_context;
|
||||
@ -117,8 +120,16 @@ ZoneMapList* TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr,
|
||||
ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr,
|
||||
Handle<String> name) {
|
||||
int arity = expr->arguments()->length();
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(
|
||||
Code::CALL_IC, NORMAL, OWN_MAP, NOT_IN_LOOP, arity);
|
||||
// Note: these flags won't let us get maps from stubs with
|
||||
// non-default extra ic state in the megamorphic case. In the more
|
||||
// important monomorphic case the map is obtained directly, so it's
|
||||
// not a problem until we decide to emit more polymorphic code.
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
|
||||
NORMAL,
|
||||
Code::kNoExtraICState,
|
||||
OWN_MAP,
|
||||
NOT_IN_LOOP,
|
||||
arity);
|
||||
return CollectReceiverTypes(expr->position(), name, flags);
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,12 @@ class TypeInfo {
|
||||
};
|
||||
|
||||
|
||||
enum StringStubFeedback {
|
||||
DEFAULT_STRING_STUB = 0,
|
||||
STRING_INDEX_OUT_OF_BOUNDS = 1
|
||||
};
|
||||
|
||||
|
||||
// Forward declarations.
|
||||
class Assignment;
|
||||
class BinaryOperation;
|
||||
|
@ -1178,8 +1178,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
Label number, non_number, non_string, boolean, probe, miss;
|
||||
|
||||
// Probe the stub cache.
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
|
||||
Code::Flags flags = Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
MONOMORPHIC,
|
||||
Code::kNoExtraICState,
|
||||
NORMAL,
|
||||
argc);
|
||||
StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, rax);
|
||||
|
||||
// If the stub cache probing failed, the receiver might be a value.
|
||||
|
@ -1327,8 +1327,8 @@ void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
|
||||
|
||||
|
||||
MaybeObject* CallStubCompiler::GenerateMissBranch() {
|
||||
MaybeObject* maybe_obj =
|
||||
StubCache::ComputeCallMiss(arguments().immediate(), kind_);
|
||||
MaybeObject* maybe_obj = StubCache::ComputeCallMiss(arguments().immediate(),
|
||||
kind_);
|
||||
Object* obj;
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
__ Jump(Handle<Code>(Code::cast(obj)), RelocInfo::CODE_TARGET);
|
||||
@ -1660,9 +1660,15 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1690,7 +1696,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_code_at_generator.GenerateFast(masm());
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
@ -1698,11 +1704,16 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_code_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(rax, Heap::kNanValueRootIndex);
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(rax, Heap::kNanValueRootIndex);
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in rcx.
|
||||
__ Move(rcx, Handle<String>(name));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
@ -1733,9 +1744,15 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
Label miss;
|
||||
Label name_miss;
|
||||
Label index_out_of_range;
|
||||
Label* index_out_of_range_label = &index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
if (kind_ == Code::CALL_IC && extra_ic_state_ == DEFAULT_STRING_STUB) {
|
||||
index_out_of_range_label = &miss;
|
||||
}
|
||||
|
||||
GenerateNameCheck(name, &name_miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1765,7 +1782,7 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
result,
|
||||
&miss, // When not a string.
|
||||
&miss, // When not a number.
|
||||
&index_out_of_range,
|
||||
index_out_of_range_label,
|
||||
STRING_INDEX_IS_NUMBER);
|
||||
char_at_generator.GenerateFast(masm());
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
@ -1773,11 +1790,16 @@ MaybeObject* CallStubCompiler::CompileStringCharAtCall(
|
||||
StubRuntimeCallHelper call_helper;
|
||||
char_at_generator.GenerateSlow(masm(), call_helper);
|
||||
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(rax, Heap::kEmptyStringRootIndex);
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
if (index_out_of_range.is_linked()) {
|
||||
__ bind(&index_out_of_range);
|
||||
__ LoadRoot(rax, Heap::kEmptyStringRootIndex);
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in rcx.
|
||||
__ Move(rcx, Handle<String>(name));
|
||||
__ bind(&name_miss);
|
||||
Object* obj;
|
||||
{ MaybeObject* maybe_obj = GenerateMissBranch();
|
||||
if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
||||
|
Loading…
Reference in New Issue
Block a user