From 3a3a27b2ee66fc3d67684a197b3391d905e7aedb Mon Sep 17 00:00:00 2001 From: "dcarney@chromium.org" Date: Fri, 6 Sep 2013 11:31:25 +0000 Subject: [PATCH] store ics for js api accessors R=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/23549019 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16571 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/stub-cache-arm.cc | 62 +++++++++++++++---- src/ia32/stub-cache-ia32.cc | 70 +++++++++++++++------ src/ic.cc | 7 +++ src/stub-cache.cc | 18 ++++++ src/stub-cache.h | 11 ++++ src/x64/stub-cache-x64.cc | 71 ++++++++++++++++------ test/cctest/test-api.cc | 144 +++++++++++++++++++++++++------------------- 7 files changed, 273 insertions(+), 110 deletions(-) diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index cb02931..c1c87d9 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -924,6 +924,35 @@ static void GenerateFastApiDirectCall(MacroAssembler* masm, } +// Generate call to api function. +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + Register receiver, + Register scratch, + int argc, + Register* values) { + ASSERT(optimization.is_simple_api_call()); + ASSERT(!receiver.is(scratch)); + + const int stack_space = kFastApiCallArguments + argc + 1; + // Assign stack space for the call arguments. + __ sub(sp, sp, Operand(stack_space * kPointerSize)); + // Write holder to stack frame. + __ str(receiver, MemOperand(sp, 0)); + // Write receiver to stack frame. + int index = stack_space - 1; + __ str(receiver, MemOperand(sp, index * kPointerSize)); + // Write the arguments to stack frame. + for (int i = 0; i < argc; i++) { + ASSERT(!receiver.is(values[i])); + ASSERT(!scratch.is(values[i])); + __ str(receiver, MemOperand(sp, index-- * kPointerSize)); + } + + GenerateFastApiDirectCall(masm, optimization, argc); +} + + class CallInterceptorCompiler BASE_EMBEDDED { public: CallInterceptorCompiler(StubCompiler* stub_compiler, @@ -1381,19 +1410,8 @@ void BaseLoadStubCompiler::GenerateLoadConstant(Handle value) { void BaseLoadStubCompiler::GenerateLoadCallback( const CallOptimization& call_optimization) { - ASSERT(call_optimization.is_simple_api_call()); - - // Assign stack space for the call arguments. - __ sub(sp, sp, Operand((kFastApiCallArguments + 1) * kPointerSize)); - - int argc = 0; - int api_call_argc = argc + kFastApiCallArguments; - // Write holder to stack frame. - __ str(receiver(), MemOperand(sp, 0)); - // Write receiver to stack frame. - __ str(receiver(), MemOperand(sp, api_call_argc * kPointerSize)); - - GenerateFastApiDirectCall(masm(), call_optimization, argc); + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch3(), 0, NULL); } @@ -2793,6 +2811,24 @@ Handle StoreStubCompiler::CompileStoreCallback( } +Handle StoreStubCompiler::CompileStoreCallback( + Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization) { + Label success; + HandlerFrontend(object, receiver(), holder, name, &success); + __ bind(&success); + + Register values[] = { value() }; + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch3(), 1, values); + + // Return the generated code. + return GetCode(kind(), Code::CALLBACKS, name); +} + + #undef __ #define __ ACCESS_MASM(masm) diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e90810e..bcf64ce 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -542,6 +542,39 @@ static void GenerateFastApiCall(MacroAssembler* masm, } +// Generate call to api function. +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + Register receiver, + Register scratch, + int argc, + Register* values) { + ASSERT(optimization.is_simple_api_call()); + ASSERT(!receiver.is(scratch)); + + const int stack_space = kFastApiCallArguments + argc + 1; + // Copy return value. + __ mov(scratch, Operand(esp, 0)); + // Assign stack space for the call arguments. + __ sub(esp, Immediate(stack_space * kPointerSize)); + // Move the return address on top of the stack. + __ mov(Operand(esp, 0), scratch); + // Write holder to stack frame. + __ mov(Operand(esp, 1 * kPointerSize), receiver); + // Write receiver to stack frame. + int index = stack_space; + __ mov(Operand(esp, index-- * kPointerSize), receiver); + // Write the arguments to stack frame. + for (int i = 0; i < argc; i++) { + ASSERT(!receiver.is(values[i])); + ASSERT(!scratch.is(values[i])); + __ mov(Operand(esp, index-- * kPointerSize), values[i]); + } + + GenerateFastApiCall(masm, optimization, argc); +} + + class CallInterceptorCompiler BASE_EMBEDDED { public: CallInterceptorCompiler(StubCompiler* stub_compiler, @@ -1356,23 +1389,8 @@ void BaseLoadStubCompiler::GenerateLoadField(Register reg, void BaseLoadStubCompiler::GenerateLoadCallback( const CallOptimization& call_optimization) { - ASSERT(call_optimization.is_simple_api_call()); - - // Copy return value. - __ mov(scratch3(), Operand(esp, 0)); - // Assign stack space for the call arguments. - __ sub(esp, Immediate((kFastApiCallArguments + 1) * kPointerSize)); - // Move the return address on top of the stack. - __ mov(Operand(esp, 0), scratch3()); - - int argc = 0; - int api_call_argc = argc + kFastApiCallArguments; - // Write holder to stack frame. - __ mov(Operand(esp, 1 * kPointerSize), receiver()); - // Write receiver to stack frame. - __ mov(Operand(esp, (api_call_argc + 1) * kPointerSize), receiver()); - - GenerateFastApiCall(masm(), call_optimization, argc); + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch3(), 0, NULL); } @@ -2901,6 +2919,24 @@ Handle StoreStubCompiler::CompileStoreCallback( } +Handle StoreStubCompiler::CompileStoreCallback( + Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization) { + Label success; + HandlerFrontend(object, receiver(), holder, name, &success); + __ bind(&success); + + Register values[] = { value() }; + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch1(), 1, values); + + // Return the generated code. + return GetCode(kind(), Code::CALLBACKS, name); +} + + #undef __ #define __ ACCESS_MASM(masm) diff --git a/src/ic.cc b/src/ic.cc index d88ab4a..9724c44 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1831,6 +1831,13 @@ Handle StoreIC::ComputeStoreMonomorphic(LookupResult* lookup, if (!setter->IsJSFunction()) break; if (holder->IsGlobalObject()) break; if (!holder->HasFastProperties()) break; + Handle function = Handle::cast(setter); + CallOptimization call_optimization(function); + if (call_optimization.is_simple_api_call() && + call_optimization.IsCompatibleReceiver(*receiver)) { + return isolate()->stub_cache()->ComputeStoreCallback( + name, receiver, holder, call_optimization, strict_mode); + } return isolate()->stub_cache()->ComputeStoreViaSetter( name, receiver, holder, Handle::cast(setter), strict_mode); diff --git a/src/stub-cache.cc b/src/stub-cache.cc index b312b1e..f83a7d2 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -631,6 +631,24 @@ Handle StubCache::ComputeStoreCallback( } +Handle StubCache::ComputeStoreCallback( + Handle name, + Handle receiver, + Handle holder, + const CallOptimization& call_optimization, + StrictModeFlag strict_mode) { + Handle stub = FindStoreHandler( + name, receiver, Code::STORE_IC, Code::CALLBACKS, strict_mode); + if (!stub.is_null()) return stub; + + StoreStubCompiler compiler(isolate_, strict_mode); + Handle handler = compiler.CompileStoreCallback( + receiver, holder, name, call_optimization); + JSObject::UpdateMapCodeCache(receiver, name, handler); + return handler; +} + + Handle StubCache::ComputeStoreViaSetter(Handle name, Handle receiver, Handle holder, diff --git a/src/stub-cache.h b/src/stub-cache.h index a267100..12afdd3 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -222,6 +222,12 @@ class StubCache { Handle callback, StrictModeFlag strict_mode); + Handle ComputeStoreCallback(Handle name, + Handle object, + Handle holder, + const CallOptimization& call_optimation, + StrictModeFlag strict_mode); + Handle ComputeStoreViaSetter(Handle name, Handle object, Handle holder, @@ -960,6 +966,11 @@ class StoreStubCompiler: public BaseStoreStubCompiler { Handle name, Handle callback); + Handle CompileStoreCallback(Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization); + static void GenerateStoreViaSetter(MacroAssembler* masm, Handle setter); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index f6cc449..31f60be 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -528,6 +528,39 @@ static void GenerateFastApiCall(MacroAssembler* masm, } +// Generate call to api function. +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + Register receiver, + Register scratch, + int argc, + Register* values) { + ASSERT(optimization.is_simple_api_call()); + ASSERT(!receiver.is(scratch)); + + const int stack_space = kFastApiCallArguments + argc + 1; + // Copy return value. + __ movq(scratch, Operand(rsp, 0)); + // Assign stack space for the call arguments. + __ subq(rsp, Immediate(stack_space * kPointerSize)); + // Move the return address on top of the stack. + __ movq(Operand(rsp, 0), scratch); + // Write holder to stack frame. + __ movq(Operand(rsp, 1 * kPointerSize), receiver); + // Write receiver to stack frame. + int index = stack_space; + __ movq(Operand(rsp, index-- * kPointerSize), receiver); + // Write the arguments to stack frame. + for (int i = 0; i < argc; i++) { + ASSERT(!receiver.is(values[i])); + ASSERT(!scratch.is(values[i])); + __ movq(Operand(rsp, index-- * kPointerSize), values[i]); + } + + GenerateFastApiCall(masm, optimization, argc); +} + + class CallInterceptorCompiler BASE_EMBEDDED { public: CallInterceptorCompiler(StubCompiler* stub_compiler, @@ -1277,24 +1310,8 @@ void BaseLoadStubCompiler::GenerateLoadField(Register reg, void BaseLoadStubCompiler::GenerateLoadCallback( const CallOptimization& call_optimization) { - ASSERT(call_optimization.is_simple_api_call()); - - // Copy return value. - __ movq(scratch3(), Operand(rsp, 0)); - // Assign stack space for the call arguments. - __ subq(rsp, Immediate((kFastApiCallArguments + 1) * kPointerSize)); - // Move the return address on top of the stack. - __ movq(Operand(rsp, 0), scratch3()); - - int argc = 0; - int api_call_argc = argc + kFastApiCallArguments; - StackArgumentsAccessor args(rsp, api_call_argc); - // Write holder to stack frame. - __ movq(args.GetArgumentOperand(api_call_argc), receiver()); - // Write receiver to stack frame. - __ movq(args.GetArgumentOperand(api_call_argc - 6), receiver()); - - GenerateFastApiCall(masm(), call_optimization, argc); + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch3(), 0, NULL); } @@ -2795,6 +2812,24 @@ Handle StoreStubCompiler::CompileStoreCallback( } +Handle StoreStubCompiler::CompileStoreCallback( + Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization) { + Label success; + HandlerFrontend(object, receiver(), holder, name, &success); + __ bind(&success); + + Register values[] = { value() }; + GenerateFastApiCall( + masm(), call_optimization, receiver(), scratch3(), 1, values); + + // Return the generated code. + return GetCode(kind(), Code::CALLBACKS, name); +} + + #undef __ #define __ ACCESS_MASM(masm) diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 9f79a43..1e572ba 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -224,78 +224,98 @@ THREADED_TEST(IsolateOfContext) { } +static void TestSignature(const char* loop_js, Local receiver) { + i::ScopedVector source(200); + i::OS::SNPrintF(source, + "for (var i = 0; i < 10; i++) {" + " %s" + "}", + loop_js); + signature_callback_count = 0; + signature_expected_receiver = receiver; + bool expected_to_throw = receiver.IsEmpty(); + v8::TryCatch try_catch; + CompileRun(source.start()); + CHECK_EQ(expected_to_throw, try_catch.HasCaught()); + if (!expected_to_throw) { + CHECK_EQ(10, signature_callback_count); + } else { + CHECK_EQ(v8_str("TypeError: Illegal invocation"), + try_catch.Exception()->ToString()); + } +} + + THREADED_TEST(ReceiverSignature) { LocalContext env; v8::HandleScope scope(env->GetIsolate()); + // Setup templates. v8::Handle fun = v8::FunctionTemplate::New(); v8::Handle sig = v8::Signature::New(fun); - fun->PrototypeTemplate()->Set( - v8_str("m"), - v8::FunctionTemplate::New(IncrementingSignatureCallback, - v8::Handle(), - sig)); - fun->PrototypeTemplate()->SetAccessorProperty( - v8_str("n"), - v8::FunctionTemplate::New(IncrementingSignatureCallback, - v8::Handle(), - sig)); - env->Global()->Set(v8_str("Fun"), fun->GetFunction()); - Local fun_instance = fun->InstanceTemplate()->NewInstance(); - env->Global()->Set(v8_str("fun_instance"), fun_instance); - signature_callback_count = 0; - int expected_count = 0; - signature_expected_receiver = fun_instance; - CompileRun( - "var o = fun_instance;" - "var key_n = 'n';" - "for (var i = 0; i < 10; i++) o.m();" - "for (var i = 0; i < 10; i++) o.n;" - "for (var i = 0; i < 10; i++) o[key_n];"); - expected_count += 30; - CHECK_EQ(expected_count, signature_callback_count); + v8::Handle callback_sig = + v8::FunctionTemplate::New( + IncrementingSignatureCallback, Local(), sig); + v8::Handle callback = + v8::FunctionTemplate::New(IncrementingSignatureCallback); v8::Handle sub_fun = v8::FunctionTemplate::New(); sub_fun->Inherit(fun); - fun_instance = sub_fun->InstanceTemplate()->NewInstance(); - env->Global()->Set(v8_str("fun_instance"), fun_instance); - signature_expected_receiver = fun_instance; - CompileRun( - "var o = fun_instance;" - "var key_n = 'n';" - "for (var i = 0; i < 10; i++) o.m();" - "for (var i = 0; i < 10; i++) o.n;" - "for (var i = 0; i < 10; i++) o[key_n];"); - expected_count += 30; - CHECK_EQ(expected_count, signature_callback_count); - v8::TryCatch try_catch; - CompileRun( - "var o = { };" - "o.m = Fun.prototype.m;" - "o.m();"); - CHECK_EQ(expected_count, signature_callback_count); - CHECK(try_catch.HasCaught()); - CompileRun( - "var o = { };" - "o.n = Fun.prototype.n;" - "o.n;"); - CHECK_EQ(expected_count, signature_callback_count); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); v8::Handle unrel_fun = v8::FunctionTemplate::New(); - sub_fun->Inherit(fun); + // Install properties. + v8::Handle fun_proto = fun->PrototypeTemplate(); + fun_proto->Set(v8_str("prop_sig"), callback_sig); + fun_proto->Set(v8_str("prop"), callback); + fun_proto->SetAccessorProperty( + v8_str("accessor_sig"), callback_sig, callback_sig); + fun_proto->SetAccessorProperty(v8_str("accessor"), callback, callback); + // Instantiate templates. + Local fun_instance = fun->InstanceTemplate()->NewInstance(); + Local sub_fun_instance = sub_fun->InstanceTemplate()->NewInstance(); + // Setup global variables. + env->Global()->Set(v8_str("Fun"), fun->GetFunction()); env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); + env->Global()->Set(v8_str("fun_instance"), fun_instance); + env->Global()->Set(v8_str("sub_fun_instance"), sub_fun_instance); CompileRun( - "var o = new UnrelFun();" - "o.m = Fun.prototype.m;" - "o.m();"); - CHECK_EQ(expected_count, signature_callback_count); - CHECK(try_catch.HasCaught()); - try_catch.Reset(); - CompileRun( - "var o = new UnrelFun();" - "o.n = Fun.prototype.n;" - "o.n;"); - CHECK_EQ(expected_count, signature_callback_count); - CHECK(try_catch.HasCaught()); + "var accessor_sig_key = 'accessor_sig';" + "var accessor_key = 'accessor';" + "var prop_sig_key = 'prop_sig';" + "var prop_key = 'prop';" + "" + "function copy_props(obj) {" + " var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];" + " var source = Fun.prototype;" + " for (var i in keys) {" + " var key = keys[i];" + " var desc = Object.getOwnPropertyDescriptor(source, key);" + " Object.defineProperty(obj, key, desc);" + " }" + "}" + "" + "var obj = {};" + "copy_props(obj);" + "var unrel = new UnrelFun();" + "copy_props(unrel);"); + // Test with and without ICs + const char* test_objects[] = { + "fun_instance", "sub_fun_instance", "obj", "unrel" }; + unsigned bad_signature_start_offset = 2; + for (unsigned i = 0; i < ARRAY_SIZE(test_objects); i++) { + i::ScopedVector source(200); + i::OS::SNPrintF( + source, "var test_object = %s; test_object", test_objects[i]); + Local test_object = CompileRun(source.start()); + TestSignature("test_object.prop();", test_object); + TestSignature("test_object.accessor;", test_object); + TestSignature("test_object[accessor_key];", test_object); + TestSignature("test_object.accessor = 1;", test_object); + TestSignature("test_object[accessor_key] = 1;", test_object); + if (i >= bad_signature_start_offset) test_object = Local(); + TestSignature("test_object.prop_sig();", test_object); + TestSignature("test_object.accessor_sig;", test_object); + TestSignature("test_object[accessor_sig_key];", test_object); + TestSignature("test_object.accessor_sig = 1;", test_object); + TestSignature("test_object[accessor_sig_key] = 1;", test_object); + } } -- 2.7.4