From ff61618c4b150e8253733385858d1024b1cc39a4 Mon Sep 17 00:00:00 2001 From: "kaznacheev@chromium.org" Date: Mon, 7 Jun 2010 08:27:32 +0000 Subject: [PATCH] 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 --- src/arm/codegen-arm.h | 2 + src/arm/ic-arm.cc | 15 +++ src/arm/stub-cache-arm.cc | 24 ++-- src/codegen.cc | 20 +++- src/debug.cc | 19 +-- src/disassembler.cc | 2 +- src/full-codegen.h | 1 + src/ia32/codegen-ia32.cc | 31 +++-- src/ia32/codegen-ia32.h | 2 + src/ia32/full-codegen-ia32.cc | 57 +++++---- src/ia32/ic-ia32.cc | 155 ++++++++++++++++++++----- src/ia32/stub-cache-ia32.cc | 60 ++++++---- src/ia32/virtual-frame-ia32.cc | 18 +++ src/ia32/virtual-frame-ia32.h | 3 + src/ic.cc | 152 ++++++++++++++++++------ src/ic.h | 53 ++++++--- src/log.cc | 4 + src/log.h | 12 ++ src/objects-inl.h | 5 +- src/objects.cc | 1 + src/objects.h | 2 + src/spaces.cc | 1 + src/stub-cache.cc | 155 +++++++++++++++++-------- src/stub-cache.h | 50 +++++--- src/x64/codegen-x64.h | 2 + src/x64/ic-x64.cc | 15 +++ src/x64/stub-cache-x64.cc | 24 ++-- test/cctest/test-api.cc | 157 +++++++++++++++++++++++++ test/cctest/test-debug.cc | 5 +- test/mjsunit/keyed-call-ic.js | 205 +++++++++++++++++++++++++++++++++ 30 files changed, 1020 insertions(+), 232 deletions(-) create mode 100644 test/mjsunit/keyed-call-ic.js diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 3c6cec317f..6a33667eb4 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -449,6 +449,8 @@ class CodeGenerator: public AstVisitor { static Handle ComputeCallInitialize(int argc, InLoopFlag in_loop); + static Handle ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); + // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle pairs); diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index a7c436d61b..5257913d21 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -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); diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 72cdc10298..3992d6c5d0 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -1019,6 +1019,12 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { } +void CallStubCompiler::GenerateMissBranch() { + Handle 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 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 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 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 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 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 ic = ComputeCallMiss(arguments().immediate()); - __ Jump(ic, RelocInfo::CODE_TARGET); + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); diff --git a/src/codegen.cc b/src/codegen.cc index f89399a974..ff2fa0395b 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -254,10 +254,28 @@ Handle 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 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* declarations) { int length = declarations->length(); int globals = 0; diff --git a/src/debug.cc b/src/debug.cc index 8cb95efd06..e279ee9eff 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -62,13 +62,14 @@ static void PrintLn(v8::Local value) { } -static Handle ComputeCallDebugBreak(int argc) { - CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc), Code); +static Handle ComputeCallDebugBreak(int argc, Code::Kind kind) { + CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugBreak(argc, kind), Code); } -static Handle ComputeCallDebugPrepareStepIn(int argc) { - CALL_HEAP_FUNCTION(StubCache::ComputeCallDebugPrepareStepIn(argc), Code); +static Handle 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::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 stub = ComputeCallDebugPrepareStepIn(code->arguments_count()); + Handle 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 Debug::FindDebugBreak(Handle 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(Builtins::builtin(Builtins::LoadIC_DebugBreak)); diff --git a/src/disassembler.cc b/src/disassembler.cc index 8473cd9f95..19cb6af728 100644 --- a/src/disassembler.cc +++ b/src/disassembler.cc @@ -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) { diff --git a/src/full-codegen.h b/src/full-codegen.h index e9a05be622..ac70d9986e 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -369,6 +369,7 @@ class FullCodeGenerator: public AstVisitor { // Platform-specific code sequences for calls void EmitCallWithStub(Call* expr); void EmitCallWithIC(Call* expr, Handle name, RelocInfo::Mode mode); + void EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode); // Platform-specific code for inline runtime calls. diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 7cd83de18b..603f9135ff 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -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 { diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index ea182ab9cc..b3ddad9d9a 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -594,6 +594,8 @@ class CodeGenerator: public AstVisitor { static Handle ComputeCallInitialize(int argc, InLoopFlag in_loop); + static Handle ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); + // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle pairs); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index ae64d023c0..28b766a572 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -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* 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 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* 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 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 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 diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 3aa60baa74..0f14a58733 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -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(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); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 0833daebf6..48d9e67454 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -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(name))); + __ j(not_equal, miss, not_taken); + } +} + + +void CallStubCompiler::GenerateMissBranch() { + Handle 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 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 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 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(name))); - Handle 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(name))); - Handle 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 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 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 ic = ComputeCallMiss(arguments().immediate()); - __ jmp(ic, RelocInfo::CODE_TARGET); + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc index e22df6ec28..36774da0dc 100644 --- a/src/ia32/virtual-frame-ia32.cc +++ b/src/ia32/virtual-frame-ia32.cc @@ -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 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 diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h index 48d0fa2471..e00626b7d4 100644 --- a/src/ia32/virtual-frame-ia32.h +++ b/src/ia32/virtual-frame-ia32.h @@ -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 diff --git a/src/ic.cc b/src/ic.cc index 678876df72..338f630064 100644 --- a/src/ic.cc +++ b/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, - Handle name) { + Handle key) { HandleScope scope; - Handle args[2] = { name, object }; + Handle args[2] = { key, object }; Handle 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 target(object); Handle delegate = Execution::GetFunctionDelegate(target); @@ -383,7 +387,7 @@ Object* CallIC::TryCallAsFunction(Object* object) { return *delegate; } -void CallIC::ReceiverToObject(Handle object) { +void CallICBase::ReceiverToObject(Handle object) { HandleScope scope; Handle receiver(object); @@ -396,9 +400,9 @@ void CallIC::ReceiverToObject(Handle object) { } -Object* CallIC::LoadFunction(State state, - Handle object, - Handle name) { +Object* CallICBase::LoadFunction(State state, + Handle object, + Handle 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, Handle 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, + Handle key) { + if (key->IsSymbol()) { + return CallICBase::LoadFunction(state, object, Handle::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, Handle 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_.cc. +static Object* CompileFunction(Object* result, + Handle object, + InLoopFlag in_loop) { + // Compile now with optimization. + HandleScope scope; + Handle function = Handle(JSFunction::cast(result)); + if (in_loop == IN_LOOP) { + CompileLazyInLoop(function, object, CLEAR_EXCEPTION); + } else { + CompileLazy(function, object, CLEAR_EXCEPTION); + } + return *function; +} + + +// Used from ic-.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 function = Handle(JSFunction::cast(result)); - InLoopFlag in_loop = ic.target()->ic_in_loop(); - if (in_loop == IN_LOOP) { - CompileLazyInLoop(function, args.at(0), CLEAR_EXCEPTION); - } else { - CompileLazy(function, args.at(0), CLEAR_EXCEPTION); - } - return *function; + return CompileFunction(result, args.at(0), ic.target()->ic_in_loop()); } -// Used from ic_.cc. +// Used from ic-.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(0), args.at(1)); + + if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { + return result; + } + return CompileFunction(result, args.at(0), ic.target()->ic_in_loop()); +} + + +// Used from ic-.cc. Object* LoadIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 2); @@ -1336,7 +1412,7 @@ Object* LoadIC_Miss(Arguments args) { } -// Used from ic_.cc +// Used from ic-.cc Object* KeyedLoadIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 2); @@ -1346,7 +1422,7 @@ Object* KeyedLoadIC_Miss(Arguments args) { } -// Used from ic_.cc. +// Used from ic-.cc. Object* StoreIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 3); @@ -1404,7 +1480,7 @@ Object* SharedStoreIC_ExtendStorage(Arguments args) { } -// Used from ic_.cc. +// Used from ic-.cc. Object* KeyedStoreIC_Miss(Arguments args) { NoHandleAllocation na; ASSERT(args.length() == 3); diff --git a/src/ic.h b/src/ic.h index a7ff6e671e..a9ad28b7fa 100644 --- a/src/ic.h +++ b/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, - Handle name); + Handle key); static Failure* ReferenceError(const char* type, Handle 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, Handle 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, Handle 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()); } diff --git a/src/log.cc b/src/log.cc index f48b358986..ada73cbe66 100644 --- a/src/log.cc +++ b/src/log.cc @@ -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)); } diff --git a/src/log.h b/src/log.h index a1441ac163..160072dec5 100644 --- a/src/log.h +++ b/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") \ diff --git a/src/objects-inl.h b/src/objects-inl.h index fe33e7ef51..c9fcaa8d64 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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()); } diff --git a/src/objects.cc b/src/objects.cc index 1aa60af4f8..1deacb5140 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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(); diff --git a/src/objects.h b/src/objects.h index 7de8b2a902..c69540ed1b 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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(); diff --git a/src/spaces.cc b/src/spaces.cc index 1d868e9ac7..3c495ba54e 100644 --- a/src/spaces.cc +++ b/src/spaces.cc @@ -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); } } diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 6ebe495f16..397988ae46 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -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 ComputeCallMiss(int argc) { - CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc), Code); +Handle 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); diff --git a/src/stub-cache.h b/src/stub-cache.h index 68187ffae0..fcfffcfc5d 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -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 ComputeCallMiss(int argc); +Handle 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(); }; diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 242667eded..823a2575f1 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -542,6 +542,8 @@ class CodeGenerator: public AstVisitor { static Handle ComputeCallInitialize(int argc, InLoopFlag in_loop); + static Handle ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); + // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle pairs); diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 21bb7e85f6..e7086a08e2 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -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; diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index eb9942cb49..cc54470529 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -706,6 +706,12 @@ static Object* GenerateCheckPropertyCell(MacroAssembler* masm, #define __ ACCESS_MASM((masm())) +void CallStubCompiler::GenerateMissBranch() { + Handle 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 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 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 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 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 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 ic = ComputeCallMiss(arguments().immediate()); - __ Jump(ic, RelocInfo::CODE_TARGET); + GenerateMissBranch(); // Return the generated code. return GetCode(NORMAL, name); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 60b991f468..503ff93b03 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -7078,6 +7078,163 @@ THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { } +v8::Handle keyed_call_ic_function; + +static v8::Handle InterceptorKeyedCallICGetter( + Local name, const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + if (v8_str("x")->Equals(name)) { + return keyed_call_ic_function; + } + return v8::Handle(); +} + + +// 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 templ = ObjectTemplate::New(); + templ->SetNamedPropertyHandler(NoBlockGetterX); + LocalContext context; + context->Global()->Set(v8_str("o"), templ->NewInstance()); + v8::Handle 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 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 = 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 templ = ObjectTemplate::New(); + templ->SetNamedPropertyHandler(NoBlockGetterX); + LocalContext context; + context->Global()->Set(v8_str("o"), templ->NewInstance()); + v8::Handle 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 templ_o = ObjectTemplate::New(); + templ_o->SetNamedPropertyHandler(NoBlockGetterX); + LocalContext context; + context->Global()->Set(v8_str("o"), templ_o->NewInstance()); + + v8::Handle 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 templ_o = ObjectTemplate::New(); + templ_o->SetNamedPropertyHandler(NoBlockGetterX); + LocalContext context; + context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); + + v8::Handle 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 templ_o = ObjectTemplate::New(); + templ_o->SetNamedPropertyHandler(NoBlockGetterX); + LocalContext context; + context->Global()->Set(v8_str("o"), templ_o->NewInstance()); + + v8::Handle 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 InterceptorICRefErrorGetter(Local name, diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 61ecfa1797..612e4fc94b 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -397,8 +397,9 @@ Handle GetDebuggedFunctions() { static Handle ComputeCallDebugBreak(int argc) { - CALL_HEAP_FUNCTION(v8::internal::StubCache::ComputeCallDebugBreak(argc), - Code); + CALL_HEAP_FUNCTION( + v8::internal::StubCache::ComputeCallDebugBreak(argc, Code::CALL_IC), + Code); } diff --git a/test/mjsunit/keyed-call-ic.js b/test/mjsunit/keyed-call-ic.js new file mode 100644 index 0000000000..9d82965f1d --- /dev/null +++ b/test/mjsunit/keyed-call-ic.js @@ -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();