Extend CallIC to support non-constant names.
This speeds up constructs like this: var zz='replace'; '123'[zz]('3','4'); Review URL: http://codereview.chromium.org/2280007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4804 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
a40569e5a0
commit
ff61618c4b
@ -449,6 +449,8 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
// Declare global variables and functions in the given array of
|
||||
// name/value pairs.
|
||||
void DeclareGlobals(Handle<FixedArray> pairs);
|
||||
|
@ -496,6 +496,21 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
// Defined in ic.cc.
|
||||
Object* LoadIC_Miss(Arguments args);
|
||||
|
||||
|
@ -1019,6 +1019,12 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
|
||||
}
|
||||
|
||||
|
||||
void CallStubCompiler::GenerateMissBranch() {
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
||||
Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
JSObject* holder,
|
||||
int index,
|
||||
@ -1045,8 +1051,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(FIELD, name);
|
||||
@ -1095,8 +1100,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1145,8 +1149,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1317,8 +1320,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
}
|
||||
|
||||
__ bind(&miss_in_smi_check);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1364,8 +1366,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(INTERCEPTOR, name);
|
||||
@ -1447,8 +1448,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(&Counters::call_global_inline_miss, 1, r1, r3);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(NORMAL, name);
|
||||
|
@ -254,10 +254,28 @@ Handle<Code> CodeGenerator::ComputeCallInitialize(
|
||||
// that it needs so we need to ensure it is generated already.
|
||||
ComputeCallInitialize(argc, NOT_IN_LOOP);
|
||||
}
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc, in_loop), Code);
|
||||
CALL_HEAP_FUNCTION(
|
||||
StubCache::ComputeCallInitialize(argc, in_loop, Code::CALL_IC),
|
||||
Code);
|
||||
}
|
||||
|
||||
|
||||
Handle<Code> CodeGenerator::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(
|
||||
StubCache::ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC),
|
||||
Code);
|
||||
}
|
||||
|
||||
void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
|
||||
int length = declarations->length();
|
||||
int globals = 0;
|
||||
|
19
src/debug.cc
19
src/debug.cc
@ -62,13 +62,14 @@ static void PrintLn(v8::Local<v8::Value> value) {
|
||||
}
|
||||
|
||||
|
||||
static Handle<Code> ComputeCallDebugBreak(int argc) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc), Code);
|
||||
static Handle<Code> ComputeCallDebugBreak(int argc, Code::Kind kind) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc, kind), Code);
|
||||
}
|
||||
|
||||
|
||||
static Handle<Code> ComputeCallDebugPrepareStepIn(int argc) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugPrepareStepIn(argc), Code);
|
||||
static Handle<Code> ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
StubCache::ComputeCallDebugPrepareStepIn(argc, kind), Code);
|
||||
}
|
||||
|
||||
|
||||
@ -360,13 +361,14 @@ void BreakLocationIterator::PrepareStepIn() {
|
||||
// construct call or CallFunction stub call.
|
||||
Address target = rinfo()->target_address();
|
||||
Handle<Code> code(Code::GetCodeFromTargetAddress(target));
|
||||
if (code->is_call_stub()) {
|
||||
if (code->is_call_stub() || code->is_keyed_call_stub()) {
|
||||
// Step in through IC call is handled by the runtime system. Therefore make
|
||||
// sure that the any current IC is cleared and the runtime system is
|
||||
// called. If the executing code has a debug break at the location change
|
||||
// the call in the original code as it is the code there that will be
|
||||
// executed in place of the debug break call.
|
||||
Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count());
|
||||
Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count(),
|
||||
code->kind());
|
||||
if (IsDebugBreak()) {
|
||||
original_rinfo()->set_target_address(stub->entry());
|
||||
} else {
|
||||
@ -1187,7 +1189,7 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
|
||||
if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
|
||||
Address target = it.rinfo()->target_address();
|
||||
Code* code = Code::GetCodeFromTargetAddress(target);
|
||||
if (code->is_call_stub()) {
|
||||
if (code->is_call_stub() || code->is_keyed_call_stub()) {
|
||||
is_call_target = true;
|
||||
}
|
||||
if (code->is_inline_cache_stub()) {
|
||||
@ -1373,7 +1375,8 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
|
||||
if (code->is_inline_cache_stub()) {
|
||||
switch (code->kind()) {
|
||||
case Code::CALL_IC:
|
||||
return ComputeCallDebugBreak(code->arguments_count());
|
||||
case Code::KEYED_CALL_IC:
|
||||
return ComputeCallDebugBreak(code->arguments_count(), code->kind());
|
||||
|
||||
case Code::LOAD_IC:
|
||||
return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
|
||||
|
@ -246,7 +246,7 @@ static int DecodeIt(FILE* f,
|
||||
if (code->ic_in_loop() == IN_LOOP) {
|
||||
out.AddFormatted(", in_loop");
|
||||
}
|
||||
if (kind == Code::CALL_IC) {
|
||||
if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
|
||||
out.AddFormatted(", argc = %d", code->arguments_count());
|
||||
}
|
||||
} else if (kind == Code::STUB) {
|
||||
|
@ -369,6 +369,7 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// Platform-specific code sequences for calls
|
||||
void EmitCallWithStub(Call* expr);
|
||||
void EmitCallWithIC(Call* expr, Handle<Object> name, RelocInfo::Mode mode);
|
||||
void EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode);
|
||||
|
||||
|
||||
// Platform-specific code for inline runtime calls.
|
||||
|
@ -5975,18 +5975,31 @@ void CodeGenerator::VisitCall(Call* node) {
|
||||
ref.GetValue();
|
||||
// Use global object as receiver.
|
||||
LoadGlobalReceiver();
|
||||
// Call the function.
|
||||
CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
|
||||
} else {
|
||||
// Push the receiver onto the frame.
|
||||
Load(property->obj());
|
||||
frame()->Dup();
|
||||
Load(property->key());
|
||||
Result function = EmitKeyedLoad();
|
||||
Result receiver = frame_->Pop();
|
||||
frame_->Push(&function);
|
||||
frame_->Push(&receiver);
|
||||
}
|
||||
|
||||
// Call the function.
|
||||
CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position());
|
||||
// Load the arguments.
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
Load(args->at(i));
|
||||
frame_->SpillTop();
|
||||
}
|
||||
|
||||
// Load the name of the function.
|
||||
Load(property->key());
|
||||
|
||||
// Call the IC initialization code.
|
||||
CodeForSourcePosition(node->position());
|
||||
Result result =
|
||||
frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET,
|
||||
arg_count,
|
||||
loop_nesting());
|
||||
frame_->RestoreContextRegister();
|
||||
frame_->Push(&result);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -594,6 +594,8 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
// Declare global variables and functions in the given array of
|
||||
// name/value pairs.
|
||||
void DeclareGlobals(Handle<FixedArray> pairs);
|
||||
|
@ -1726,6 +1726,29 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
|
||||
Expression* key,
|
||||
RelocInfo::Mode mode) {
|
||||
// Code common for calls using the IC.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
int arg_count = args->length();
|
||||
for (int i = 0; i < arg_count; i++) {
|
||||
VisitForValue(args->at(i), kStack);
|
||||
}
|
||||
VisitForValue(key, kAccumulator);
|
||||
__ mov(ecx, eax);
|
||||
// Record source position of the IC call.
|
||||
SetSourcePosition(expr->position());
|
||||
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
|
||||
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
|
||||
arg_count, in_loop);
|
||||
__ call(ic, mode);
|
||||
// Restore context register.
|
||||
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
|
||||
Apply(context_, eax);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitCallWithStub(Call* expr) {
|
||||
// Code common for calls using the call stub.
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
@ -1815,37 +1838,31 @@ void FullCodeGenerator::VisitCall(Call* expr) {
|
||||
VisitForValue(prop->obj(), kStack);
|
||||
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
|
||||
} else {
|
||||
// Call to a keyed property, use keyed load IC followed by function
|
||||
// call.
|
||||
// Call to a keyed property.
|
||||
// For a synthetic property use keyed load IC followed by function call,
|
||||
// for a regular property use keyed CallIC.
|
||||
VisitForValue(prop->obj(), kStack);
|
||||
VisitForValue(prop->key(), kAccumulator);
|
||||
// Record source code position for IC call.
|
||||
SetSourcePosition(prop->position());
|
||||
if (prop->is_synthetic()) {
|
||||
VisitForValue(prop->key(), kAccumulator);
|
||||
// Record source code position for IC call.
|
||||
SetSourcePosition(prop->position());
|
||||
__ pop(edx); // We do not need to keep the receiver.
|
||||
} else {
|
||||
__ mov(edx, Operand(esp, 0)); // Keep receiver, to call function on.
|
||||
}
|
||||
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
||||
__ call(ic, RelocInfo::CODE_TARGET);
|
||||
// By emitting a nop we make sure that we do not have a "test eax,..."
|
||||
// instruction after the call it is treated specially by the LoadIC code.
|
||||
__ nop();
|
||||
if (prop->is_synthetic()) {
|
||||
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
|
||||
__ call(ic, RelocInfo::CODE_TARGET);
|
||||
// By emitting a nop we make sure that we do not have a "test eax,..."
|
||||
// instruction after the call as it is treated specially
|
||||
// by the LoadIC code.
|
||||
__ nop();
|
||||
// Push result (function).
|
||||
__ push(eax);
|
||||
// Push Global receiver.
|
||||
__ mov(ecx, CodeGenerator::GlobalObject());
|
||||
__ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
|
||||
EmitCallWithStub(expr);
|
||||
} else {
|
||||
// Pop receiver.
|
||||
__ pop(ebx);
|
||||
// Push result (function).
|
||||
__ push(eax);
|
||||
__ push(ebx);
|
||||
EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
|
||||
}
|
||||
EmitCallWithStub(expr);
|
||||
}
|
||||
} else {
|
||||
// Call to some other expression. If the expression is an anonymous
|
||||
|
@ -1044,22 +1044,21 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
|
||||
// Defined in ic.cc.
|
||||
Object* CallIC_Miss(Arguments args);
|
||||
|
||||
void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// The generated code does not accept smi keys.
|
||||
// The generated code falls through if both probes miss.
|
||||
static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
int argc,
|
||||
Code::Kind kind,
|
||||
Label* miss) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -- edx : receiver
|
||||
// -----------------------------------
|
||||
Label number, non_number, non_string, boolean, probe, miss;
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
Label number, non_number, non_string, boolean, probe;
|
||||
|
||||
// Probe the stub cache.
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
|
||||
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, eax);
|
||||
|
||||
// If the stub cache probing failed, the receiver might be a value.
|
||||
@ -1079,7 +1078,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
|
||||
// Check for string.
|
||||
__ bind(&non_number);
|
||||
__ cmp(ebx, FIRST_NONSTRING_TYPE);
|
||||
__ CmpInstanceType(ebx, FIRST_NONSTRING_TYPE);
|
||||
__ j(above_equal, &non_string, taken);
|
||||
StubCompiler::GenerateLoadGlobalFunctionPrototype(
|
||||
masm, Context::STRING_FUNCTION_INDEX, edx);
|
||||
@ -1090,7 +1089,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
__ cmp(edx, Factory::true_value());
|
||||
__ j(equal, &boolean, not_taken);
|
||||
__ cmp(edx, Factory::false_value());
|
||||
__ j(not_equal, &miss, taken);
|
||||
__ j(not_equal, miss, taken);
|
||||
__ bind(&boolean);
|
||||
StubCompiler::GenerateLoadGlobalFunctionPrototype(
|
||||
masm, Context::BOOLEAN_FUNCTION_INDEX, edx);
|
||||
@ -1098,10 +1097,6 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// Probe the stub cache for the value object.
|
||||
__ bind(&probe);
|
||||
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, no_reg);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
@ -1141,8 +1136,8 @@ static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
// The generated code never falls through.
|
||||
static void GenerateCallNormal(MacroAssembler* masm, int argc, Label* miss) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
@ -1150,20 +1145,20 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
Label miss, global_object, non_global_object;
|
||||
Label global_object, non_global_object;
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss, not_taken);
|
||||
__ j(zero, miss, not_taken);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
|
||||
__ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(eax, FIRST_JS_OBJECT_TYPE);
|
||||
__ j(below, &miss, not_taken);
|
||||
__ j(below, miss, not_taken);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
@ -1179,8 +1174,8 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
// Check that the global object does not require access checks.
|
||||
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
GenerateNormalHelper(masm, argc, true, &miss);
|
||||
__ j(not_equal, miss, not_taken);
|
||||
GenerateNormalHelper(masm, argc, true, miss);
|
||||
|
||||
// Accessing non-global object: Check for access to global proxy.
|
||||
Label global_proxy, invoke;
|
||||
@ -1191,22 +1186,18 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
// require access checks.
|
||||
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
__ j(not_equal, miss, not_taken);
|
||||
__ bind(&invoke);
|
||||
GenerateNormalHelper(masm, argc, false, &miss);
|
||||
GenerateNormalHelper(masm, argc, false, miss);
|
||||
|
||||
// Global object proxy access: Check access rights.
|
||||
__ bind(&global_proxy);
|
||||
__ CheckAccessGlobalProxy(edx, eax, &miss);
|
||||
__ CheckAccessGlobalProxy(edx, eax, miss);
|
||||
__ jmp(&invoke);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
@ -1228,7 +1219,7 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
// Call the entry.
|
||||
CEntryStub stub(1);
|
||||
__ mov(eax, Immediate(2));
|
||||
__ mov(ebx, Immediate(ExternalReference(IC_Utility(kCallIC_Miss))));
|
||||
__ mov(ebx, Immediate(ExternalReference(IC_Utility(id))));
|
||||
__ CallStub(&stub);
|
||||
|
||||
// Move result to edi and exit the internal frame.
|
||||
@ -1259,6 +1250,106 @@ void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
}
|
||||
|
||||
|
||||
void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
Label miss;
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, &miss);
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
Label miss;
|
||||
GenerateCallNormal(masm, argc, &miss);
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
Label miss, skip_probe;
|
||||
|
||||
// Do not probe monomorphic cache if a key is a smi.
|
||||
__ test(ecx, Immediate(kSmiTagMask));
|
||||
__ j(equal, &skip_probe, taken);
|
||||
|
||||
GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC, &skip_probe);
|
||||
|
||||
__ bind(&skip_probe);
|
||||
|
||||
__ mov(eax, ecx);
|
||||
__ EnterInternalFrame();
|
||||
__ push(ecx);
|
||||
__ call(Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_Generic)),
|
||||
RelocInfo::CODE_TARGET);
|
||||
__ pop(ecx);
|
||||
__ LeaveInternalFrame();
|
||||
__ mov(edi, eax);
|
||||
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss, not_taken);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, eax);
|
||||
__ j(below, &miss, not_taken);
|
||||
|
||||
// Check that the value is a JavaScript function.
|
||||
__ test(edi, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss, not_taken);
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
||||
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
Label miss;
|
||||
GenerateCallNormal(masm, argc, &miss);
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
// Defined in ic.cc.
|
||||
Object* LoadIC_Miss(Arguments args);
|
||||
|
||||
|
@ -1040,6 +1040,20 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
|
||||
}
|
||||
|
||||
|
||||
void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
|
||||
if (kind_ == Code::KEYED_CALL_IC) {
|
||||
__ cmp(Operand(ecx), Immediate(Handle<String>(name)));
|
||||
__ j(not_equal, miss, not_taken);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CallStubCompiler::GenerateMissBranch() {
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
||||
Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
JSObject* holder,
|
||||
int index,
|
||||
@ -1053,6 +1067,8 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
// -----------------------------------
|
||||
Label miss;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Get the receiver from the stack.
|
||||
const int argc = arguments().immediate();
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
@ -1084,8 +1100,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(FIELD, name);
|
||||
@ -1113,6 +1128,8 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
|
||||
Label miss;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Get the receiver from the stack.
|
||||
const int argc = arguments().immediate();
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
@ -1230,8 +1247,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
}
|
||||
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1259,6 +1275,8 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
||||
|
||||
Label miss, return_undefined, call_builtin;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Get the receiver from the stack.
|
||||
const int argc = arguments().immediate();
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
@ -1312,8 +1330,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
||||
1);
|
||||
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1337,6 +1354,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
|
||||
|
||||
Label miss;
|
||||
Label index_out_of_range;
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
@ -1346,7 +1364,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
|
||||
ebx, edx, name, &miss);
|
||||
|
||||
Register receiver = ebx;
|
||||
Register index = ecx;
|
||||
Register index = edi;
|
||||
Register scratch = edx;
|
||||
Register result = eax;
|
||||
__ mov(receiver, Operand(esp, (argc + 1) * kPointerSize));
|
||||
@ -1375,11 +1393,8 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
|
||||
__ ret((argc + 1) * kPointerSize);
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in ecx.
|
||||
__ Set(ecx, Immediate(Handle<String>(name)));
|
||||
|
||||
Handle<Code> ic = ComputeCallMiss(argc);
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1404,6 +1419,8 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
|
||||
Label miss;
|
||||
Label index_out_of_range;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Check that the maps starting from the prototype haven't changed.
|
||||
GenerateDirectLoadGlobalFunctionPrototype(masm(),
|
||||
Context::STRING_FUNCTION_INDEX,
|
||||
@ -1412,7 +1429,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
|
||||
ebx, edx, name, &miss);
|
||||
|
||||
Register receiver = eax;
|
||||
Register index = ecx;
|
||||
Register index = edi;
|
||||
Register scratch1 = ebx;
|
||||
Register scratch2 = edx;
|
||||
Register result = eax;
|
||||
@ -1444,10 +1461,8 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
|
||||
|
||||
__ bind(&miss);
|
||||
// Restore function name in ecx.
|
||||
__ Set(ecx, Immediate(Handle<String>(name)));
|
||||
|
||||
Handle<Code> ic = ComputeCallMiss(argc);
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1480,6 +1495,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
|
||||
Label miss_in_smi_check;
|
||||
|
||||
GenerateNameCheck(name, &miss_in_smi_check);
|
||||
|
||||
// Get the receiver from the stack.
|
||||
const int argc = arguments().immediate();
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
@ -1599,8 +1616,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
FreeSpaceForFastApiCall(masm(), eax);
|
||||
}
|
||||
__ bind(&miss_in_smi_check);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1619,6 +1635,8 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
||||
// -----------------------------------
|
||||
Label miss;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Get the number of arguments.
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
@ -1661,8 +1679,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
||||
|
||||
// Handle load cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(argc);
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(INTERCEPTOR, name);
|
||||
@ -1683,6 +1700,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
|
||||
// -----------------------------------
|
||||
Label miss;
|
||||
|
||||
GenerateNameCheck(name, &miss);
|
||||
|
||||
// Get the number of arguments.
|
||||
const int argc = arguments().immediate();
|
||||
|
||||
@ -1745,8 +1764,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(&Counters::call_global_inline_miss, 1);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(NORMAL, name);
|
||||
|
@ -1119,6 +1119,24 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
|
||||
}
|
||||
|
||||
|
||||
Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode,
|
||||
int arg_count,
|
||||
int loop_nesting) {
|
||||
// Function name, arguments, and receiver are on top of the frame.
|
||||
// The IC expects the name in ecx and the rest on the stack and
|
||||
// drops them all.
|
||||
InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
|
||||
Handle<Code> ic = cgen()->ComputeKeyedCallInitialize(arg_count, in_loop);
|
||||
// Spill args, receiver, and function. The call will drop args and
|
||||
// receiver.
|
||||
Result name = Pop();
|
||||
PrepareForCall(arg_count + 1, arg_count + 1); // Arguments + receiver.
|
||||
name.ToRegister(ecx);
|
||||
name.Unuse();
|
||||
return RawCallCodeObject(ic, mode);
|
||||
}
|
||||
|
||||
|
||||
Result VirtualFrame::CallConstructor(int arg_count) {
|
||||
// Arguments, receiver, and function are on top of the frame. The
|
||||
// IC expects arg count in eax, function in edi, and the arguments
|
||||
|
@ -360,6 +360,9 @@ class VirtualFrame: public ZoneObject {
|
||||
// include the receiver.
|
||||
Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
|
||||
|
||||
// Call keyed call IC. Same calling convention as CallCallIC.
|
||||
Result CallKeyedCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
|
||||
|
||||
// Allocate and call JS function as constructor. Arguments,
|
||||
// receiver (global object), and function are found on top of the
|
||||
// frame. Function is not dropped. The argument count does not
|
||||
|
152
src/ic.cc
152
src/ic.cc
@ -152,11 +152,13 @@ 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, the most likely cause of cache failure is
|
||||
// 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) {
|
||||
if (kind == Code::KEYED_LOAD_IC ||
|
||||
kind == Code::KEYED_STORE_IC ||
|
||||
kind == Code::KEYED_CALL_IC) {
|
||||
return MONOMORPHIC;
|
||||
}
|
||||
|
||||
@ -196,9 +198,9 @@ RelocInfo::Mode IC::ComputeMode() {
|
||||
|
||||
Failure* IC::TypeError(const char* type,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
Handle<Object> key) {
|
||||
HandleScope scope;
|
||||
Handle<Object> args[2] = { name, object };
|
||||
Handle<Object> args[2] = { key, object };
|
||||
Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2));
|
||||
return Top::Throw(*error);
|
||||
}
|
||||
@ -224,6 +226,7 @@ void IC::Clear(Address address) {
|
||||
case Code::STORE_IC: return StoreIC::Clear(address, target);
|
||||
case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
|
||||
case Code::CALL_IC: return CallIC::Clear(address, target);
|
||||
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
|
||||
case Code::BINARY_OP_IC: return; // Clearing these is tricky and does not
|
||||
// make any performance difference.
|
||||
default: UNREACHABLE();
|
||||
@ -231,12 +234,13 @@ void IC::Clear(Address address) {
|
||||
}
|
||||
|
||||
|
||||
void CallIC::Clear(Address address, Code* target) {
|
||||
void CallICBase::Clear(Address address, Code* target) {
|
||||
State state = target->ic_state();
|
||||
InLoopFlag in_loop = target->ic_in_loop();
|
||||
if (state == UNINITIALIZED) return;
|
||||
Code* code =
|
||||
StubCache::FindCallInitialize(target->arguments_count(), in_loop);
|
||||
StubCache::FindCallInitialize(target->arguments_count(),
|
||||
target->ic_in_loop(),
|
||||
target->kind());
|
||||
SetTargetAtAddress(address, code);
|
||||
}
|
||||
|
||||
@ -364,7 +368,7 @@ static void LookupForRead(Object* object,
|
||||
}
|
||||
|
||||
|
||||
Object* CallIC::TryCallAsFunction(Object* object) {
|
||||
Object* CallICBase::TryCallAsFunction(Object* object) {
|
||||
HandleScope scope;
|
||||
Handle<Object> target(object);
|
||||
Handle<Object> delegate = Execution::GetFunctionDelegate(target);
|
||||
@ -383,7 +387,7 @@ Object* CallIC::TryCallAsFunction(Object* object) {
|
||||
return *delegate;
|
||||
}
|
||||
|
||||
void CallIC::ReceiverToObject(Handle<Object> object) {
|
||||
void CallICBase::ReceiverToObject(Handle<Object> object) {
|
||||
HandleScope scope;
|
||||
Handle<Object> receiver(object);
|
||||
|
||||
@ -396,9 +400,9 @@ void CallIC::ReceiverToObject(Handle<Object> object) {
|
||||
}
|
||||
|
||||
|
||||
Object* CallIC::LoadFunction(State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
Object* CallICBase::LoadFunction(State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
// If the object is undefined or null it's illegal to try to get any
|
||||
// of its properties; throw a TypeError in that case.
|
||||
if (object->IsUndefined() || object->IsNull()) {
|
||||
@ -481,7 +485,7 @@ Object* CallIC::LoadFunction(State state,
|
||||
}
|
||||
|
||||
|
||||
void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
void CallICBase::UpdateCaches(LookupResult* lookup,
|
||||
State state,
|
||||
Handle<Object> object,
|
||||
Handle<String> name) {
|
||||
@ -497,16 +501,21 @@ void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
// This is the first time we execute this inline cache.
|
||||
// Set the target to the pre monomorphic stub to delay
|
||||
// setting the monomorphic state.
|
||||
code = StubCache::ComputeCallPreMonomorphic(argc, in_loop);
|
||||
code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
|
||||
} else if (state == MONOMORPHIC) {
|
||||
code = StubCache::ComputeCallMegamorphic(argc, in_loop);
|
||||
code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
|
||||
} else {
|
||||
// Compute monomorphic stub.
|
||||
switch (lookup->type()) {
|
||||
case FIELD: {
|
||||
int index = lookup->GetFieldIndex();
|
||||
code = StubCache::ComputeCallField(argc, in_loop, *name, *object,
|
||||
lookup->holder(), index);
|
||||
code = StubCache::ComputeCallField(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
index);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_FUNCTION: {
|
||||
@ -514,8 +523,13 @@ void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
// call; used for rewriting to monomorphic state and making sure
|
||||
// that the code stub is in the stub cache.
|
||||
JSFunction* function = lookup->GetConstantFunction();
|
||||
code = StubCache::ComputeCallConstant(argc, in_loop, *name, *object,
|
||||
lookup->holder(), function);
|
||||
code = StubCache::ComputeCallConstant(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder(),
|
||||
function);
|
||||
break;
|
||||
}
|
||||
case NORMAL: {
|
||||
@ -530,6 +544,7 @@ void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
JSFunction* function = JSFunction::cast(cell->value());
|
||||
code = StubCache::ComputeCallGlobal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver,
|
||||
global,
|
||||
@ -541,13 +556,20 @@ void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
// property must be found in the receiver for the stub to be
|
||||
// applicable.
|
||||
if (lookup->holder() != *receiver) return;
|
||||
code = StubCache::ComputeCallNormal(argc, in_loop, *name, *receiver);
|
||||
code = StubCache::ComputeCallNormal(argc,
|
||||
in_loop,
|
||||
kind_,
|
||||
*name,
|
||||
*receiver);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INTERCEPTOR: {
|
||||
ASSERT(HasInterceptorGetter(lookup->holder()));
|
||||
code = StubCache::ComputeCallInterceptor(argc, *name, *object,
|
||||
code = StubCache::ComputeCallInterceptor(argc,
|
||||
kind_,
|
||||
*name,
|
||||
*object,
|
||||
lookup->holder());
|
||||
break;
|
||||
}
|
||||
@ -569,11 +591,44 @@ void CallIC::UpdateCaches(LookupResult* lookup,
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
TraceIC("CallIC", name, state, target(), in_loop ? " (in-loop)" : "");
|
||||
TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
|
||||
name, state, target(), in_loop ? " (in-loop)" : "");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Object* KeyedCallIC::LoadFunction(State state,
|
||||
Handle<Object> object,
|
||||
Handle<Object> key) {
|
||||
if (key->IsSymbol()) {
|
||||
return CallICBase::LoadFunction(state, object, Handle<String>::cast(key));
|
||||
}
|
||||
|
||||
if (object->IsUndefined() || object->IsNull()) {
|
||||
return TypeError("non_object_property_call", object, key);
|
||||
}
|
||||
|
||||
if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
|
||||
ReceiverToObject(object);
|
||||
} else {
|
||||
if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
|
||||
int argc = target()->arguments_count();
|
||||
InLoopFlag in_loop = target()->ic_in_loop();
|
||||
Object* code = StubCache::ComputeCallMegamorphic(
|
||||
argc, in_loop, Code::KEYED_CALL_IC);
|
||||
if (!code->IsFailure()) {
|
||||
set_target(Code::cast(code));
|
||||
}
|
||||
}
|
||||
}
|
||||
Object* result = Runtime::GetObjectProperty(object, key);
|
||||
if (result->IsJSFunction()) return result;
|
||||
result = TryCallAsFunction(result);
|
||||
return result->IsJSFunction() ?
|
||||
result : TypeError("property_not_function", object, key);
|
||||
}
|
||||
|
||||
|
||||
Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
|
||||
// If the object is undefined or null it's illegal to try to get any
|
||||
// of its properties; throw a TypeError in that case.
|
||||
@ -1293,7 +1348,22 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
|
||||
// Static IC stub generators.
|
||||
//
|
||||
|
||||
// Used from ic_<arch>.cc.
|
||||
static Object* CompileFunction(Object* result,
|
||||
Handle<Object> object,
|
||||
InLoopFlag in_loop) {
|
||||
// Compile now with optimization.
|
||||
HandleScope scope;
|
||||
Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
|
||||
if (in_loop == IN_LOOP) {
|
||||
CompileLazyInLoop(function, object, CLEAR_EXCEPTION);
|
||||
} else {
|
||||
CompileLazy(function, object, CLEAR_EXCEPTION);
|
||||
}
|
||||
return *function;
|
||||
}
|
||||
|
||||
|
||||
// Used from ic-<arch>.cc.
|
||||
Object* CallIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 2);
|
||||
@ -1312,21 +1382,27 @@ Object* CallIC_Miss(Arguments args) {
|
||||
if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Compile now with optimization.
|
||||
HandleScope scope;
|
||||
Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
|
||||
InLoopFlag in_loop = ic.target()->ic_in_loop();
|
||||
if (in_loop == IN_LOOP) {
|
||||
CompileLazyInLoop(function, args.at<Object>(0), CLEAR_EXCEPTION);
|
||||
} else {
|
||||
CompileLazy(function, args.at<Object>(0), CLEAR_EXCEPTION);
|
||||
}
|
||||
return *function;
|
||||
return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
|
||||
}
|
||||
|
||||
|
||||
// Used from ic_<arch>.cc.
|
||||
// Used from ic-<arch>.cc.
|
||||
Object* KeyedCallIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 2);
|
||||
KeyedCallIC ic;
|
||||
IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
|
||||
Object* result =
|
||||
ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
|
||||
|
||||
if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
|
||||
return result;
|
||||
}
|
||||
return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
|
||||
}
|
||||
|
||||
|
||||
// Used from ic-<arch>.cc.
|
||||
Object* LoadIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 2);
|
||||
@ -1336,7 +1412,7 @@ Object* LoadIC_Miss(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
// Used from ic_<arch>.cc
|
||||
// Used from ic-<arch>.cc
|
||||
Object* KeyedLoadIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 2);
|
||||
@ -1346,7 +1422,7 @@ Object* KeyedLoadIC_Miss(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
// Used from ic_<arch>.cc.
|
||||
// Used from ic-<arch>.cc.
|
||||
Object* StoreIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 3);
|
||||
@ -1404,7 +1480,7 @@ Object* SharedStoreIC_ExtendStorage(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
// Used from ic_<arch>.cc.
|
||||
// Used from ic-<arch>.cc.
|
||||
Object* KeyedStoreIC_Miss(Arguments args) {
|
||||
NoHandleAllocation na;
|
||||
ASSERT(args.length() == 3);
|
||||
|
53
src/ic.h
53
src/ic.h
@ -44,6 +44,7 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE };
|
||||
ICU(LoadIC_Miss) \
|
||||
ICU(KeyedLoadIC_Miss) \
|
||||
ICU(CallIC_Miss) \
|
||||
ICU(KeyedCallIC_Miss) \
|
||||
ICU(StoreIC_Miss) \
|
||||
ICU(StoreIC_ArrayLength) \
|
||||
ICU(SharedStoreIC_ExtendStorage) \
|
||||
@ -147,7 +148,7 @@ class IC {
|
||||
|
||||
static Failure* TypeError(const char* type,
|
||||
Handle<Object> object,
|
||||
Handle<String> name);
|
||||
Handle<Object> key);
|
||||
static Failure* ReferenceError(const char* type, Handle<String> name);
|
||||
|
||||
// Access the target code for the given IC address.
|
||||
@ -184,22 +185,16 @@ class IC_Utility {
|
||||
};
|
||||
|
||||
|
||||
class CallIC: public IC {
|
||||
public:
|
||||
CallIC() : IC(EXTRA_CALL_FRAME) { ASSERT(target()->is_call_stub()); }
|
||||
class CallICBase: public IC {
|
||||
protected:
|
||||
explicit CallICBase(Code::Kind kind) : IC(EXTRA_CALL_FRAME), kind_(kind) {}
|
||||
|
||||
public:
|
||||
Object* LoadFunction(State state, Handle<Object> object, Handle<String> name);
|
||||
|
||||
protected:
|
||||
Code::Kind kind_;
|
||||
|
||||
// Code generator routines.
|
||||
static void GenerateInitialize(MacroAssembler* masm, int argc) {
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
static void GenerateMiss(MacroAssembler* masm, int argc);
|
||||
static void GenerateMegamorphic(MacroAssembler* masm, int argc);
|
||||
static void GenerateNormal(MacroAssembler* masm, int argc);
|
||||
|
||||
private:
|
||||
// Update the inline cache and the global stub cache based on the
|
||||
// lookup result.
|
||||
void UpdateCaches(LookupResult* lookup,
|
||||
@ -219,6 +214,38 @@ class CallIC: public IC {
|
||||
};
|
||||
|
||||
|
||||
class CallIC: public CallICBase {
|
||||
public:
|
||||
CallIC() : CallICBase(Code::CALL_IC) { ASSERT(target()->is_call_stub()); }
|
||||
|
||||
// Code generator routines.
|
||||
static void GenerateInitialize(MacroAssembler* masm, int argc) {
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
static void GenerateMiss(MacroAssembler* masm, int argc);
|
||||
static void GenerateMegamorphic(MacroAssembler* masm, int argc);
|
||||
static void GenerateNormal(MacroAssembler* masm, int argc);
|
||||
};
|
||||
|
||||
|
||||
class KeyedCallIC: public CallICBase {
|
||||
public:
|
||||
KeyedCallIC() : CallICBase(Code::KEYED_CALL_IC) {
|
||||
ASSERT(target()->is_keyed_call_stub());
|
||||
}
|
||||
|
||||
Object* LoadFunction(State state, Handle<Object> object, Handle<Object> key);
|
||||
|
||||
// Code generator routines.
|
||||
static void GenerateInitialize(MacroAssembler* masm, int argc) {
|
||||
GenerateMiss(masm, argc);
|
||||
}
|
||||
static void GenerateMiss(MacroAssembler* masm, int argc);
|
||||
static void GenerateMegamorphic(MacroAssembler* masm, int argc);
|
||||
static void GenerateNormal(MacroAssembler* masm, int argc);
|
||||
};
|
||||
|
||||
|
||||
class LoadIC: public IC {
|
||||
public:
|
||||
LoadIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_load_stub()); }
|
||||
|
@ -1295,6 +1295,10 @@ void Logger::LogCodeObject(Object* object) {
|
||||
description = "A call IC from the snapshot";
|
||||
tag = Logger::CALL_IC_TAG;
|
||||
break;
|
||||
case Code::KEYED_CALL_IC:
|
||||
description = "A keyed call IC from the snapshot";
|
||||
tag = Logger::KEYED_CALL_IC_TAG;
|
||||
break;
|
||||
}
|
||||
PROFILE(CodeCreateEvent(tag, code_object, description));
|
||||
}
|
||||
|
12
src/log.h
12
src/log.h
@ -106,6 +106,18 @@ class CompressionHelper;
|
||||
V(CALL_MISS_TAG, "CallMiss", "cm") \
|
||||
V(CALL_NORMAL_TAG, "CallNormal", "cn") \
|
||||
V(CALL_PRE_MONOMORPHIC_TAG, "CallPreMonomorphic", "cpm") \
|
||||
V(KEYED_CALL_DEBUG_BREAK_TAG, "KeyedCallDebugBreak", "kcdb") \
|
||||
V(KEYED_CALL_DEBUG_PREPARE_STEP_IN_TAG, \
|
||||
"KeyedCallDebugPrepareStepIn", \
|
||||
"kcdbsi") \
|
||||
V(KEYED_CALL_IC_TAG, "KeyedCallIC", "kcic") \
|
||||
V(KEYED_CALL_INITIALIZE_TAG, "KeyedCallInitialize", "kci") \
|
||||
V(KEYED_CALL_MEGAMORPHIC_TAG, "KeyedCallMegamorphic", "kcmm") \
|
||||
V(KEYED_CALL_MISS_TAG, "KeyedCallMiss", "kcm") \
|
||||
V(KEYED_CALL_NORMAL_TAG, "KeyedCallNormal", "kcn") \
|
||||
V(KEYED_CALL_PRE_MONOMORPHIC_TAG, \
|
||||
"KeyedCallPreMonomorphic", \
|
||||
"kcpm") \
|
||||
V(CALLBACK_TAG, "Callback", "cb") \
|
||||
V(EVAL_TAG, "Eval", "e") \
|
||||
V(FUNCTION_TAG, "Function", "f") \
|
||||
|
@ -2196,7 +2196,8 @@ Code::Flags Code::flags() {
|
||||
void Code::set_flags(Code::Flags flags) {
|
||||
STATIC_ASSERT(Code::NUMBER_OF_KINDS <= (kFlagsKindMask >> kFlagsKindShift)+1);
|
||||
// Make sure that all call stubs have an arguments count.
|
||||
ASSERT(ExtractKindFromFlags(flags) != CALL_IC ||
|
||||
ASSERT((ExtractKindFromFlags(flags) != CALL_IC &&
|
||||
ExtractKindFromFlags(flags) != KEYED_CALL_IC) ||
|
||||
ExtractArgumentsCountFromFlags(flags) >= 0);
|
||||
WRITE_INT_FIELD(this, kFlagsOffset, flags);
|
||||
}
|
||||
@ -2232,7 +2233,7 @@ PropertyType Code::type() {
|
||||
|
||||
|
||||
int Code::arguments_count() {
|
||||
ASSERT(is_call_stub() || kind() == STUB);
|
||||
ASSERT(is_call_stub() || is_keyed_call_stub() || kind() == STUB);
|
||||
return ExtractArgumentsCountFromFlags(flags());
|
||||
}
|
||||
|
||||
|
@ -5402,6 +5402,7 @@ const char* Code::Kind2String(Kind kind) {
|
||||
case STORE_IC: return "STORE_IC";
|
||||
case KEYED_STORE_IC: return "KEYED_STORE_IC";
|
||||
case CALL_IC: return "CALL_IC";
|
||||
case KEYED_CALL_IC: return "KEYED_CALL_IC";
|
||||
case BINARY_OP_IC: return "BINARY_OP_IC";
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -2669,6 +2669,7 @@ class Code: public HeapObject {
|
||||
LOAD_IC,
|
||||
KEYED_LOAD_IC,
|
||||
CALL_IC,
|
||||
KEYED_CALL_IC,
|
||||
STORE_IC,
|
||||
KEYED_STORE_IC,
|
||||
BINARY_OP_IC,
|
||||
@ -2723,6 +2724,7 @@ class Code: public HeapObject {
|
||||
inline bool is_store_stub() { return kind() == STORE_IC; }
|
||||
inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; }
|
||||
inline bool is_call_stub() { return kind() == CALL_IC; }
|
||||
inline bool is_keyed_call_stub() { return kind() == KEYED_CALL_IC; }
|
||||
|
||||
// [major_key]: For kind STUB or BINARY_OP_IC, the major key.
|
||||
inline CodeStub::Major major_key();
|
||||
|
@ -1457,6 +1457,7 @@ static void ReportCodeKindStatistics() {
|
||||
CASE(STORE_IC);
|
||||
CASE(KEYED_STORE_IC);
|
||||
CASE(CALL_IC);
|
||||
CASE(KEYED_CALL_IC);
|
||||
CASE(BINARY_OP_IC);
|
||||
}
|
||||
}
|
||||
|
@ -441,9 +441,12 @@ Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver,
|
||||
return code;
|
||||
}
|
||||
|
||||
#define CALL_LOGGER_TAG(kind, type) \
|
||||
(kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type)
|
||||
|
||||
Object* StubCache::ComputeCallConstant(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
@ -462,7 +465,7 @@ Object* StubCache::ComputeCallConstant(int argc,
|
||||
}
|
||||
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(Code::CALL_IC,
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
CONSTANT_FUNCTION,
|
||||
in_loop,
|
||||
argc);
|
||||
@ -474,11 +477,12 @@ Object* 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);
|
||||
CallStubCompiler compiler(argc, in_loop, kind);
|
||||
code = compiler.CompileCallConstant(object, holder, function, name, check);
|
||||
if (code->IsFailure()) return code;
|
||||
ASSERT_EQ(flags, Code::cast(code)->flags());
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
|
||||
Code::cast(code), name));
|
||||
Object* result = map->UpdateCodeCache(name, Code::cast(code));
|
||||
if (result->IsFailure()) return result;
|
||||
}
|
||||
@ -488,6 +492,7 @@ Object* StubCache::ComputeCallConstant(int argc,
|
||||
|
||||
Object* StubCache::ComputeCallField(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
@ -502,20 +507,21 @@ Object* StubCache::ComputeCallField(int argc,
|
||||
object = holder;
|
||||
}
|
||||
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
|
||||
FIELD,
|
||||
in_loop,
|
||||
argc);
|
||||
Object* code = map->FindInCodeCache(name, flags);
|
||||
if (code->IsUndefined()) {
|
||||
CallStubCompiler compiler(argc, in_loop);
|
||||
CallStubCompiler compiler(argc, in_loop, kind);
|
||||
code = compiler.CompileCallField(JSObject::cast(object),
|
||||
holder,
|
||||
index,
|
||||
name);
|
||||
if (code->IsFailure()) return code;
|
||||
ASSERT_EQ(flags, Code::cast(code)->flags());
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
|
||||
Code::cast(code), name));
|
||||
Object* result = map->UpdateCodeCache(name, Code::cast(code));
|
||||
if (result->IsFailure()) return result;
|
||||
}
|
||||
@ -524,6 +530,7 @@ Object* StubCache::ComputeCallField(int argc,
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallInterceptor(int argc,
|
||||
Code::Kind kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder) {
|
||||
@ -539,19 +546,20 @@ Object* StubCache::ComputeCallInterceptor(int argc,
|
||||
}
|
||||
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(Code::CALL_IC,
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
INTERCEPTOR,
|
||||
NOT_IN_LOOP,
|
||||
argc);
|
||||
Object* code = map->FindInCodeCache(name, flags);
|
||||
if (code->IsUndefined()) {
|
||||
CallStubCompiler compiler(argc, NOT_IN_LOOP);
|
||||
CallStubCompiler compiler(argc, NOT_IN_LOOP, kind);
|
||||
code = compiler.CompileCallInterceptor(JSObject::cast(object),
|
||||
holder,
|
||||
name);
|
||||
if (code->IsFailure()) return code;
|
||||
ASSERT_EQ(flags, Code::cast(code)->flags());
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
|
||||
Code::cast(code), name));
|
||||
Object* result = map->UpdateCodeCache(name, Code::cast(code));
|
||||
if (result->IsFailure()) return result;
|
||||
}
|
||||
@ -561,9 +569,10 @@ Object* StubCache::ComputeCallInterceptor(int argc,
|
||||
|
||||
Object* StubCache::ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
String* name,
|
||||
JSObject* receiver) {
|
||||
Object* code = ComputeCallNormal(argc, in_loop);
|
||||
Object* code = ComputeCallNormal(argc, in_loop, kind);
|
||||
if (code->IsFailure()) return code;
|
||||
return Set(name, receiver->map(), Code::cast(code));
|
||||
}
|
||||
@ -571,13 +580,17 @@ Object* StubCache::ComputeCallNormal(int argc,
|
||||
|
||||
Object* StubCache::ComputeCallGlobal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind,
|
||||
String* name,
|
||||
JSObject* receiver,
|
||||
GlobalObject* holder,
|
||||
JSGlobalPropertyCell* cell,
|
||||
JSFunction* function) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeMonomorphicFlags(Code::CALL_IC, NORMAL, in_loop, argc);
|
||||
Code::ComputeMonomorphicFlags(kind,
|
||||
NORMAL,
|
||||
in_loop,
|
||||
argc);
|
||||
Object* code = receiver->map()->FindInCodeCache(name, flags);
|
||||
if (code->IsUndefined()) {
|
||||
// If the function hasn't been compiled yet, we cannot do it now
|
||||
@ -585,11 +598,12 @@ Object* 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);
|
||||
CallStubCompiler compiler(argc, in_loop, kind);
|
||||
code = compiler.CompileCallGlobal(receiver, holder, cell, function, name);
|
||||
if (code->IsFailure()) return code;
|
||||
ASSERT_EQ(flags, Code::cast(code)->flags());
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
|
||||
Code::cast(code), name));
|
||||
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
|
||||
if (result->IsFailure()) return result;
|
||||
}
|
||||
@ -637,9 +651,11 @@ static Object* FillCache(Object* code) {
|
||||
}
|
||||
|
||||
|
||||
Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
|
||||
Code* StubCache::FindCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Object* result = ProbeCache(flags);
|
||||
ASSERT(!result->IsUndefined());
|
||||
// This might be called during the marking phase of the collector
|
||||
@ -648,9 +664,11 @@ Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
|
||||
Object* StubCache::ComputeCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, in_loop, UNINITIALIZED, NORMAL, argc);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -658,9 +676,11 @@ Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
|
||||
Object* StubCache::ComputeCallPreMonomorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, in_loop, PREMONOMORPHIC, NORMAL, argc);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -668,9 +688,11 @@ Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
|
||||
Object* StubCache::ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, in_loop, MONOMORPHIC, NORMAL, argc);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -678,9 +700,11 @@ Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
|
||||
Object* StubCache::ComputeCallMegamorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, in_loop, MEGAMORPHIC, NORMAL, argc);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -688,9 +712,11 @@ Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallMiss(int argc) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc);
|
||||
Object* 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);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -699,9 +725,9 @@ Object* StubCache::ComputeCallMiss(int argc) {
|
||||
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
Object* StubCache::ComputeCallDebugBreak(int argc) {
|
||||
Object* StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
|
||||
Code::ComputeFlags(kind, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
|
||||
Object* probe = ProbeCache(flags);
|
||||
if (!probe->IsUndefined()) return probe;
|
||||
StubCompiler compiler;
|
||||
@ -709,9 +735,9 @@ Object* StubCache::ComputeCallDebugBreak(int argc) {
|
||||
}
|
||||
|
||||
|
||||
Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) {
|
||||
Object* StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) {
|
||||
Code::Flags flags =
|
||||
Code::ComputeFlags(Code::CALL_IC,
|
||||
Code::ComputeFlags(kind,
|
||||
NOT_IN_LOOP,
|
||||
DEBUG_PREPARE_STEP_IN,
|
||||
NORMAL,
|
||||
@ -758,8 +784,8 @@ void StubCache::Clear() {
|
||||
|
||||
|
||||
// Support function for computing call IC miss stubs.
|
||||
Handle<Code> ComputeCallMiss(int argc) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc), Code);
|
||||
Handle<Code> ComputeCallMiss(int argc, Code::Kind kind) {
|
||||
CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc, kind), Code);
|
||||
}
|
||||
|
||||
|
||||
@ -966,13 +992,18 @@ Object* KeyedLoadPropertyWithInterceptor(Arguments args) {
|
||||
Object* StubCompiler::CompileCallInitialize(Code::Flags flags) {
|
||||
HandleScope scope;
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
CallIC::GenerateInitialize(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateInitialize(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateInitialize(masm(), argc);
|
||||
}
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallInitialize");
|
||||
if (!result->IsFailure()) {
|
||||
Counters::call_initialize_stubs.Increment();
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_INITIALIZE_TAG,
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -984,13 +1015,18 @@ Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
// The code of the PreMonomorphic stub is the same as the code
|
||||
// of the Initialized stub. They just differ on the code object flags.
|
||||
CallIC::GenerateInitialize(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateInitialize(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateInitialize(masm(), argc);
|
||||
}
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallPreMonomorphic");
|
||||
if (!result->IsFailure()) {
|
||||
Counters::call_premonomorphic_stubs.Increment();
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_PRE_MONOMORPHIC_TAG,
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -1000,13 +1036,18 @@ Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
|
||||
Object* StubCompiler::CompileCallNormal(Code::Flags flags) {
|
||||
HandleScope scope;
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
CallIC::GenerateNormal(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateNormal(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateNormal(masm(), argc);
|
||||
}
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallNormal");
|
||||
if (!result->IsFailure()) {
|
||||
Counters::call_normal_stubs.Increment();
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_NORMAL_TAG,
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -1016,13 +1057,19 @@ Object* StubCompiler::CompileCallNormal(Code::Flags flags) {
|
||||
Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
|
||||
HandleScope scope;
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
CallIC::GenerateMegamorphic(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateMegamorphic(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateMegamorphic(masm(), argc);
|
||||
}
|
||||
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallMegamorphic");
|
||||
if (!result->IsFailure()) {
|
||||
Counters::call_megamorphic_stubs.Increment();
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_MEGAMORPHIC_TAG,
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -1032,13 +1079,18 @@ Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
|
||||
Object* StubCompiler::CompileCallMiss(Code::Flags flags) {
|
||||
HandleScope scope;
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
CallIC::GenerateMiss(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateMiss(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateMiss(masm(), argc);
|
||||
}
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallMiss");
|
||||
if (!result->IsFailure()) {
|
||||
Counters::call_megamorphic_stubs.Increment();
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_MISS_TAG,
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -1053,7 +1105,8 @@ Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) {
|
||||
if (!result->IsFailure()) {
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_DEBUG_BREAK_TAG,
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_DEBUG_BREAK_TAG),
|
||||
code, code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
@ -1065,18 +1118,26 @@ Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) {
|
||||
// Use the same code for the the step in preparations as we do for
|
||||
// the miss case.
|
||||
int argc = Code::ExtractArgumentsCountFromFlags(flags);
|
||||
CallIC::GenerateMiss(masm(), argc);
|
||||
Code::Kind kind = Code::ExtractKindFromFlags(flags);
|
||||
if (kind == Code::CALL_IC) {
|
||||
CallIC::GenerateMiss(masm(), argc);
|
||||
} else {
|
||||
KeyedCallIC::GenerateMiss(masm(), argc);
|
||||
}
|
||||
Object* result = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn");
|
||||
if (!result->IsFailure()) {
|
||||
Code* code = Code::cast(result);
|
||||
USE(code);
|
||||
PROFILE(CodeCreateEvent(Logger::CALL_DEBUG_PREPARE_STEP_IN_TAG,
|
||||
code, code->arguments_count()));
|
||||
PROFILE(CodeCreateEvent(
|
||||
CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG),
|
||||
code,
|
||||
code->arguments_count()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CALL_LOGGER_TAG
|
||||
|
||||
Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) {
|
||||
// Check for allocation failures during stub compilation.
|
||||
@ -1167,7 +1228,7 @@ Object* CallStubCompiler::CompileCustomCall(int generator_id,
|
||||
|
||||
Object* CallStubCompiler::GetCode(PropertyType type, String* name) {
|
||||
int argc = arguments_.immediate();
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
|
||||
Code::Flags flags = Code::ComputeMonomorphicFlags(kind_,
|
||||
type,
|
||||
in_loop_,
|
||||
argc);
|
||||
|
@ -142,6 +142,7 @@ class StubCache : public AllStatic {
|
||||
|
||||
static Object* ComputeCallField(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
@ -149,6 +150,7 @@ class StubCache : public AllStatic {
|
||||
|
||||
static Object* ComputeCallConstant(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder,
|
||||
@ -156,16 +158,19 @@ class StubCache : public AllStatic {
|
||||
|
||||
static Object* ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
JSObject* receiver);
|
||||
|
||||
static Object* ComputeCallInterceptor(int argc,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
Object* object,
|
||||
JSObject* holder);
|
||||
|
||||
static Object* ComputeCallGlobal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind,
|
||||
String* name,
|
||||
JSObject* receiver,
|
||||
GlobalObject* holder,
|
||||
@ -174,18 +179,33 @@ class StubCache : public AllStatic {
|
||||
|
||||
// ---
|
||||
|
||||
static Object* ComputeCallInitialize(int argc, InLoopFlag in_loop);
|
||||
static Object* ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop);
|
||||
static Object* ComputeCallNormal(int argc, InLoopFlag in_loop);
|
||||
static Object* ComputeCallMegamorphic(int argc, InLoopFlag in_loop);
|
||||
static Object* ComputeCallMiss(int argc);
|
||||
static Object* ComputeCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind);
|
||||
|
||||
static Object* ComputeCallPreMonomorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind);
|
||||
|
||||
static Object* ComputeCallNormal(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind);
|
||||
|
||||
static Object* ComputeCallMegamorphic(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind);
|
||||
|
||||
static Object* ComputeCallMiss(int argc, Code::Kind kind);
|
||||
|
||||
// Finds the Code object stored in the Heap::non_monomorphic_cache().
|
||||
static Code* FindCallInitialize(int argc, InLoopFlag in_loop);
|
||||
static Code* FindCallInitialize(int argc,
|
||||
InLoopFlag in_loop,
|
||||
Code::Kind kind);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
static Object* ComputeCallDebugBreak(int argc);
|
||||
static Object* ComputeCallDebugPrepareStepIn(int argc);
|
||||
static Object* ComputeCallDebugBreak(int argc, Code::Kind kind);
|
||||
|
||||
static Object* ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind);
|
||||
#endif
|
||||
|
||||
static Object* ComputeLazyCompile(int argc);
|
||||
@ -197,9 +217,6 @@ class StubCache : public AllStatic {
|
||||
// Clear the lookup table (@ mark compact collection).
|
||||
static void Clear();
|
||||
|
||||
// Functions for generating stubs at startup.
|
||||
static void GenerateMiss(MacroAssembler* masm);
|
||||
|
||||
// Generate code for probing the stub cache table.
|
||||
// If extra != no_reg it might be used as am extra scratch register.
|
||||
static void GenerateProbe(MacroAssembler* masm,
|
||||
@ -318,7 +335,7 @@ Object* KeyedLoadPropertyWithInterceptor(Arguments args);
|
||||
|
||||
|
||||
// Support function for computing call IC miss stubs.
|
||||
Handle<Code> ComputeCallMiss(int argc);
|
||||
Handle<Code> ComputeCallMiss(int argc, Code::Kind kind);
|
||||
|
||||
|
||||
// The stub compiler compiles stubs for the stub cache.
|
||||
@ -594,8 +611,8 @@ class CallStubCompiler: public StubCompiler {
|
||||
kNumCallGenerators
|
||||
};
|
||||
|
||||
CallStubCompiler(int argc, InLoopFlag in_loop)
|
||||
: arguments_(argc), in_loop_(in_loop) { }
|
||||
CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind)
|
||||
: arguments_(argc), in_loop_(in_loop), kind_(kind) { }
|
||||
|
||||
Object* CompileCallField(JSObject* object,
|
||||
JSObject* holder,
|
||||
@ -635,6 +652,7 @@ class CallStubCompiler: public StubCompiler {
|
||||
private:
|
||||
const ParameterCount arguments_;
|
||||
const InLoopFlag in_loop_;
|
||||
const Code::Kind kind_;
|
||||
|
||||
const ParameterCount& arguments() { return arguments_; }
|
||||
|
||||
@ -643,6 +661,10 @@ class CallStubCompiler: public StubCompiler {
|
||||
// Convenience function. Calls GetCode above passing
|
||||
// CONSTANT_FUNCTION type and the name of the given function.
|
||||
Object* GetCode(JSFunction* function);
|
||||
|
||||
void GenerateNameCheck(String* name, Label* miss);
|
||||
|
||||
void GenerateMissBranch();
|
||||
};
|
||||
|
||||
|
||||
|
@ -542,6 +542,8 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
|
||||
|
||||
// Declare global variables and functions in the given array of
|
||||
// name/value pairs.
|
||||
void DeclareGlobals(Handle<FixedArray> pairs);
|
||||
|
@ -1330,6 +1330,21 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
// The offset from the inlined patch site to the start of the
|
||||
// inlined load instruction.
|
||||
const int LoadIC::kOffsetToLoadInstruction = 20;
|
||||
|
@ -706,6 +706,12 @@ static Object* GenerateCheckPropertyCell(MacroAssembler* masm,
|
||||
|
||||
#define __ ACCESS_MASM((masm()))
|
||||
|
||||
void CallStubCompiler::GenerateMissBranch() {
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate(), kind_);
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
}
|
||||
|
||||
|
||||
Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
JSObject* holder,
|
||||
JSFunction* function,
|
||||
@ -853,8 +859,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss_in_smi_check);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -905,8 +910,7 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
|
||||
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(FIELD, name);
|
||||
@ -1060,8 +1064,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
|
||||
__ bind(&miss);
|
||||
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1145,8 +1148,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
|
||||
1);
|
||||
__ bind(&miss);
|
||||
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ jmp(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(function);
|
||||
@ -1229,8 +1231,7 @@ Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
||||
|
||||
// Handle load cache miss.
|
||||
__ bind(&miss);
|
||||
Handle<Code> ic = ComputeCallMiss(argc);
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(INTERCEPTOR, name);
|
||||
@ -1313,8 +1314,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
|
||||
// Handle call cache miss.
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(&Counters::call_global_inline_miss, 1);
|
||||
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
||||
__ Jump(ic, RelocInfo::CODE_TARGET);
|
||||
GenerateMissBranch();
|
||||
|
||||
// Return the generated code.
|
||||
return GetCode(NORMAL, name);
|
||||
|
@ -7078,6 +7078,163 @@ THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) {
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<Value> keyed_call_ic_function;
|
||||
|
||||
static v8::Handle<Value> InterceptorKeyedCallICGetter(
|
||||
Local<String> name, const AccessorInfo& info) {
|
||||
ApiTestFuzzer::Fuzz();
|
||||
if (v8_str("x")->Equals(name)) {
|
||||
return keyed_call_ic_function;
|
||||
}
|
||||
return v8::Handle<Value>();
|
||||
}
|
||||
|
||||
|
||||
// Test the case when we stored cacheable lookup into
|
||||
// a stub, but the function name changed (to another cacheable function).
|
||||
THREADED_TEST(InterceptorKeyedCallICKeyChange1) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetNamedPropertyHandler(NoBlockGetterX);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("o"), templ->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"proto = new Object();"
|
||||
"proto.y = function(x) { return x + 1; };"
|
||||
"proto.z = function(x) { return x - 1; };"
|
||||
"o.__proto__ = proto;"
|
||||
"var result = 0;"
|
||||
"var method = 'y';"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) { method = 'z'; };"
|
||||
" result += o[method](41);"
|
||||
"}");
|
||||
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
// Test the case when we stored cacheable lookup into
|
||||
// a stub, but the function name changed (and the new function is present
|
||||
// both before and after the interceptor in the prototype chain).
|
||||
THREADED_TEST(InterceptorKeyedCallICKeyChange2) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("proto1"), templ->NewInstance());
|
||||
keyed_call_ic_function =
|
||||
v8_compile("function f(x) { return x - 1; }; f")->Run();
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"o = new Object();"
|
||||
"proto2 = new Object();"
|
||||
"o.y = function(x) { return x + 1; };"
|
||||
"proto2.y = function(x) { return x + 2; };"
|
||||
"o.__proto__ = proto1;"
|
||||
"proto1.__proto__ = proto2;"
|
||||
"var result = 0;"
|
||||
"var method = 'x';"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) { method = 'y'; };"
|
||||
" result += o[method](41);"
|
||||
"}");
|
||||
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit
|
||||
// on the global object.
|
||||
THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetNamedPropertyHandler(NoBlockGetterX);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("o"), templ->NewInstance());
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"function inc(x) { return x + 1; };"
|
||||
"inc(1);"
|
||||
"function dec(x) { return x - 1; };"
|
||||
"dec(1);"
|
||||
"o.__proto__ = this;"
|
||||
"this.__proto__.x = inc;"
|
||||
"this.__proto__.y = dec;"
|
||||
"var result = 0;"
|
||||
"var method = 'x';"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) { method = 'y'; };"
|
||||
" result += o[method](41);"
|
||||
"}");
|
||||
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
// Test the case when actual function to call sits on global object.
|
||||
THREADED_TEST(InterceptorKeyedCallICFromGlobal) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
||||
templ_o->SetNamedPropertyHandler(NoBlockGetterX);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
||||
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"function len(x) { return x.length; };"
|
||||
"o.__proto__ = this;"
|
||||
"var m = 'parseFloat';"
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) {"
|
||||
" m = 'len';"
|
||||
" saved_result = result;"
|
||||
" };"
|
||||
" result = o[m]('239');"
|
||||
"}");
|
||||
CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value());
|
||||
}
|
||||
|
||||
// Test the map transition before the interceptor.
|
||||
THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
||||
templ_o->SetNamedPropertyHandler(NoBlockGetterX);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("proto"), templ_o->NewInstance());
|
||||
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"var o = new Object();"
|
||||
"o.__proto__ = proto;"
|
||||
"o.method = function(x) { return x + 1; };"
|
||||
"var m = 'method';"
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) { o.method = function(x) { return x - 1; }; };"
|
||||
" result += o[m](41);"
|
||||
"}");
|
||||
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
// Test the map transition after the interceptor.
|
||||
THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) {
|
||||
v8::HandleScope scope;
|
||||
v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New();
|
||||
templ_o->SetNamedPropertyHandler(NoBlockGetterX);
|
||||
LocalContext context;
|
||||
context->Global()->Set(v8_str("o"), templ_o->NewInstance());
|
||||
|
||||
v8::Handle<Value> value = CompileRun(
|
||||
"var proto = new Object();"
|
||||
"o.__proto__ = proto;"
|
||||
"proto.method = function(x) { return x + 1; };"
|
||||
"var m = 'method';"
|
||||
"var result = 0;"
|
||||
"for (var i = 0; i < 10; i++) {"
|
||||
" if (i == 5) { proto.method = function(x) { return x - 1; }; };"
|
||||
" result += o[m](41);"
|
||||
"}");
|
||||
CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value());
|
||||
}
|
||||
|
||||
|
||||
static int interceptor_call_count = 0;
|
||||
|
||||
static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name,
|
||||
|
@ -397,8 +397,9 @@ Handle<FixedArray> GetDebuggedFunctions() {
|
||||
|
||||
|
||||
static Handle<Code> ComputeCallDebugBreak(int argc) {
|
||||
CALL_HEAP_FUNCTION(v8::internal::StubCache::ComputeCallDebugBreak(argc),
|
||||
Code);
|
||||
CALL_HEAP_FUNCTION(
|
||||
v8::internal::StubCache::ComputeCallDebugBreak(argc, Code::CALL_IC),
|
||||
Code);
|
||||
}
|
||||
|
||||
|
||||
|
205
test/mjsunit/keyed-call-ic.js
Normal file
205
test/mjsunit/keyed-call-ic.js
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// A test for keyed call ICs.
|
||||
|
||||
var toStringName = 'toString';
|
||||
var global = this;
|
||||
|
||||
function globalFunction1() {
|
||||
return 'function1';
|
||||
}
|
||||
|
||||
function globalFunction2() {
|
||||
return 'function2';
|
||||
}
|
||||
|
||||
assertEquals("[object global]", this[toStringName]());
|
||||
assertEquals("[object global]", global[toStringName]());
|
||||
|
||||
function testGlobals() {
|
||||
assertEquals("[object global]", this[toStringName]());
|
||||
assertEquals("[object global]", global[toStringName]());
|
||||
}
|
||||
|
||||
testGlobals();
|
||||
|
||||
|
||||
function F() {}
|
||||
|
||||
F.prototype.one = function() {return 'one'; }
|
||||
F.prototype.two = function() {return 'two'; }
|
||||
F.prototype.three = function() {return 'three'; }
|
||||
|
||||
var keys =
|
||||
['one', 'one', 'one', 'one', 'two', 'two', 'one', 'three', 'one', 'two'];
|
||||
|
||||
function testKeyTransitions() {
|
||||
var i, key, result, message;
|
||||
|
||||
var f = new F();
|
||||
|
||||
// Custom call generators
|
||||
var array = [];
|
||||
for (i = 0; i != 10; i++) {
|
||||
key = (i < 8) ? 'push' : 'pop';
|
||||
array[key](i);
|
||||
}
|
||||
|
||||
assertEquals(6, array.length);
|
||||
for (i = 0; i != array.length; i++) {
|
||||
assertEquals(i, array[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i != 10; i++) {
|
||||
key = (i < 3) ? 'pop' : 'push';
|
||||
array[key](i);
|
||||
}
|
||||
|
||||
assertEquals(10, array.length);
|
||||
for (i = 0; i != array.length; i++) {
|
||||
assertEquals(i, array[i]);
|
||||
}
|
||||
|
||||
var string = 'ABCDEFGHIJ';
|
||||
for (i = 0; i != 10; i++) {
|
||||
key = ((i < 5) ? 'charAt' : 'charCodeAt');
|
||||
result = string[key](i);
|
||||
message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
|
||||
if (i < 5) {
|
||||
assertEquals(string.charAt(i), result, message);
|
||||
} else {
|
||||
assertEquals(string.charCodeAt(i), result, message);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i != 10; i++) {
|
||||
key = ((i < 5) ? 'charCodeAt' : 'charAt');
|
||||
result = string[key](i);
|
||||
message = '\'' + string + '\'[\'' + key + '\'](' + i + ')';
|
||||
if (i < 5) {
|
||||
assertEquals(string.charCodeAt(i), result, message);
|
||||
} else {
|
||||
assertEquals(string.charAt(i), result, message);
|
||||
}
|
||||
}
|
||||
|
||||
// Function is a constant property
|
||||
key = 'one';
|
||||
for (i = 0; i != 10; i++) {
|
||||
assertEquals(key, f[key]());
|
||||
if (i == 5) {
|
||||
key = 'two'; // the name change should case a miss
|
||||
}
|
||||
}
|
||||
|
||||
// Function is a fast property
|
||||
f.field = function() { return 'field'; }
|
||||
key = 'field';
|
||||
for (i = 0; i != 10; i++) {
|
||||
assertEquals(key, f[key]());
|
||||
if (i == 5) {
|
||||
key = 'two'; // the name change should case a miss
|
||||
}
|
||||
}
|
||||
|
||||
// Calling on slow case object
|
||||
f.prop = 0;
|
||||
delete f.prop; // force the object to the slow case
|
||||
f.four = function() { return 'four'; }
|
||||
f.five = function() { return 'five'; }
|
||||
|
||||
key = 'four';
|
||||
for (i = 0; i != 10; i++) {
|
||||
assertEquals(key, f[key]());
|
||||
if (i == 5) {
|
||||
key = 'five';
|
||||
}
|
||||
}
|
||||
|
||||
// Calling on global object
|
||||
key = 'globalFunction1';
|
||||
var expect = 'function1';
|
||||
for (i = 0; i != 10; i++) {
|
||||
assertEquals(expect, global[key]());
|
||||
if (i == 5) {
|
||||
key = 'globalFunction2';
|
||||
expect = 'function2';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testKeyTransitions();
|
||||
|
||||
function testTypeTransitions() {
|
||||
var f = new F();
|
||||
var s = '';
|
||||
var m = 'one';
|
||||
var i;
|
||||
|
||||
s = '';
|
||||
for (i = 0; i != 10; i++) {
|
||||
if (i == 5) { F.prototype.one = function() { return '1'; } }
|
||||
s += f[m]();
|
||||
}
|
||||
assertEquals("oneoneoneoneone11111", s);
|
||||
|
||||
s = '';
|
||||
for (i = 0; i != 10; i++) {
|
||||
if (i == 5) { f.__proto__ = { one: function() { return 'I'; } } }
|
||||
s += f[m]();
|
||||
}
|
||||
assertEquals("11111IIIII", s);
|
||||
|
||||
s = '';
|
||||
for (i = 0; i != 10; i++) {
|
||||
if (i == 5) { f.one = function() { return 'ONE'; } }
|
||||
s += f[m]();
|
||||
}
|
||||
assertEquals("IIIIIONEONEONEONEONE", s);
|
||||
|
||||
m = 'toString';
|
||||
|
||||
s = '';
|
||||
var obj = { toString: function() { return '2'; } };
|
||||
for (i = 0; i != 10; i++) {
|
||||
if (i == 5) { obj = "TWO"; }
|
||||
s += obj[m]();
|
||||
}
|
||||
assertEquals("22222TWOTWOTWOTWOTWO", s);
|
||||
|
||||
s = '';
|
||||
obj = { toString: function() { return 'ONE'; } };
|
||||
m = 'toString';
|
||||
for (i = 0; i != 10; i++) {
|
||||
if (i == 5) { obj = 1; }
|
||||
s += obj[m]();
|
||||
}
|
||||
assertEquals("ONEONEONEONEONE11111", s);
|
||||
}
|
||||
|
||||
testTypeTransitions();
|
Loading…
Reference in New Issue
Block a user