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:
kaznacheev@chromium.org 2010-06-07 08:27:32 +00:00
parent a40569e5a0
commit ff61618c4b
30 changed files with 1020 additions and 232 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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) {

View File

@ -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.

View File

@ -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 {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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
View File

@ -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);

View File

@ -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()); }

View File

@ -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));
}

View File

@ -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") \

View File

@ -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());
}

View File

@ -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();

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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();
};

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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);
}

View 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();