From 11af95d74989a0a36d343ac475a733ee7d2b0a8f Mon Sep 17 00:00:00 2001 From: "dcarney@chromium.org" Date: Thu, 5 Sep 2013 08:34:17 +0000 Subject: [PATCH] load ics for js api accessors R=mstarzinger@chromium.org BUG= Review URL: https://codereview.chromium.org/23699002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16543 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/stub-cache-arm.cc | 20 +++++++++++++++- src/ia32/stub-cache-ia32.cc | 24 ++++++++++++++++++- src/ic.cc | 39 +++++++++++++++++++++++------- src/stub-cache.cc | 55 +++++++++++++++++++++++++++++++++++++++++++ src/stub-cache.h | 29 ++++++++++++++++++++--- src/x64/stub-cache-x64.cc | 25 +++++++++++++++++++- test/cctest/test-accessors.cc | 17 +++++++++---- test/cctest/test-api.cc | 54 ++++++++++++++++++++++++++++++++++-------- 8 files changed, 234 insertions(+), 29 deletions(-) diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 7605525..6ee4110 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -1292,7 +1292,7 @@ Register BaseLoadStubCompiler::CallbackHandlerFrontend( Handle holder, Handle name, Label* success, - Handle callback) { + Handle callback) { Label miss; Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss); @@ -1380,6 +1380,24 @@ 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); +} + + +void BaseLoadStubCompiler::GenerateLoadCallback( Register reg, Handle callback) { // Build AccessorInfo::args_ list on the stack and push property name below diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 9aa7bae..e90810e 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1261,7 +1261,7 @@ Register BaseLoadStubCompiler::CallbackHandlerFrontend( Handle holder, Handle name, Label* success, - Handle callback) { + Handle callback) { Label miss; Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss); @@ -1355,6 +1355,28 @@ 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); +} + + +void BaseLoadStubCompiler::GenerateLoadCallback( Register reg, Handle callback) { // Insert additional parameters into the stack frame above return address. diff --git a/src/ic.cc b/src/ic.cc index 190fdb1..44e13e1 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1357,8 +1357,15 @@ Handle LoadIC::ComputeLoadHandler(LookupResult* lookup, if (!getter->IsJSFunction()) break; if (holder->IsGlobalObject()) break; if (!holder->HasFastProperties()) break; + Handle function = Handle::cast(getter); + CallOptimization call_optimization(function); + if (call_optimization.is_simple_api_call() && + call_optimization.IsCompatibleReceiver(*receiver)) { + return isolate()->stub_cache()->ComputeLoadCallback( + name, receiver, holder, call_optimization); + } return isolate()->stub_cache()->ComputeLoadViaGetter( - name, receiver, holder, Handle::cast(getter)); + name, receiver, holder, function); } else if (receiver->IsJSArray() && name->Equals(isolate()->heap()->length_string())) { PropertyIndex lengthIndex = @@ -1544,13 +1551,29 @@ Handle KeyedLoadIC::ComputeLoadHandler(LookupResult* lookup, case CALLBACKS: { Handle callback_object(lookup->GetCallbackObject(), isolate()); // TODO(dcarney): Handle DeclaredAccessorInfo correctly. - if (!callback_object->IsExecutableAccessorInfo()) break; - Handle callback = - Handle::cast(callback_object); - if (v8::ToCData
(callback->getter()) == 0) break; - if (!callback->IsCompatibleReceiver(*receiver)) break; - return isolate()->stub_cache()->ComputeKeyedLoadCallback( - name, receiver, holder, callback); + if (callback_object->IsExecutableAccessorInfo()) { + Handle callback = + Handle::cast(callback_object); + if (v8::ToCData
(callback->getter()) == 0) break; + if (!callback->IsCompatibleReceiver(*receiver)) break; + return isolate()->stub_cache()->ComputeKeyedLoadCallback( + name, receiver, holder, callback); + } else if (callback_object->IsAccessorPair()) { + Handle getter( + Handle::cast(callback_object)->getter(), + isolate()); + if (!getter->IsJSFunction()) break; + if (holder->IsGlobalObject()) break; + if (!holder->HasFastProperties()) break; + Handle function = Handle::cast(getter); + CallOptimization call_optimization(function); + if (call_optimization.is_simple_api_call() && + call_optimization.IsCompatibleReceiver(*receiver)) { + return isolate()->stub_cache()->ComputeKeyedLoadCallback( + name, receiver, holder, call_optimization); + } + } + break; } case INTERCEPTOR: ASSERT(HasInterceptorGetter(lookup->holder())); diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 19cfd5a..c7c6bc3 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -315,6 +315,24 @@ Handle StubCache::ComputeLoadCallback( } +Handle StubCache::ComputeLoadCallback( + Handle name, + Handle receiver, + Handle holder, + const CallOptimization& call_optimization) { + Handle stub_holder = StubHolder(receiver, holder); + Handle stub = FindLoadHandler( + name, receiver, stub_holder, Code::LOAD_IC, Code::CALLBACKS); + if (!stub.is_null()) return stub; + + LoadStubCompiler compiler(isolate_); + Handle handler = + compiler.CompileLoadCallback(receiver, holder, name, call_optimization); + JSObject::UpdateMapCodeCache(stub_holder, name, handler); + return handler; +} + + Handle StubCache::ComputeLoadViaGetter(Handle name, Handle receiver, Handle holder, @@ -465,6 +483,24 @@ Handle StubCache::ComputeKeyedLoadCallback( } +Handle StubCache::ComputeKeyedLoadCallback( + Handle name, + Handle receiver, + Handle holder, + const CallOptimization& call_optimization) { + Handle stub_holder = StubHolder(receiver, holder); + Handle stub = FindLoadHandler( + name, receiver, stub_holder, Code::KEYED_LOAD_IC, Code::CALLBACKS); + if (!stub.is_null()) return stub; + + KeyedLoadStubCompiler compiler(isolate_); + Handle handler = + compiler.CompileLoadCallback(receiver, holder, name, call_optimization); + JSObject::UpdateMapCodeCache(stub_holder, name, handler); + return handler; +} + + Handle StubCache::ComputeStoreField(Handle name, Handle receiver, LookupResult* lookup, @@ -1648,6 +1684,25 @@ Handle BaseLoadStubCompiler::CompileLoadCallback( } +Handle BaseLoadStubCompiler::CompileLoadCallback( + Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization) { + ASSERT(call_optimization.is_simple_api_call()); + Label success; + + Handle callback = call_optimization.constant_function(); + CallbackHandlerFrontend( + object, receiver(), holder, name, &success, callback); + __ bind(&success); + GenerateLoadCallback(call_optimization); + + // Return the generated code. + return GetCode(kind(), Code::CALLBACKS, name); +} + + Handle BaseLoadStubCompiler::CompileLoadInterceptor( Handle object, Handle holder, diff --git a/src/stub-cache.h b/src/stub-cache.h index 25009b9..a267100 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -48,6 +48,8 @@ namespace internal { // invalidate the cache whenever a prototype map is changed. The stub // validates the map chain as in the mono-morphic case. + +class CallOptimization; class SmallMapList; class StubCache; @@ -136,6 +138,11 @@ class StubCache { Handle holder, Handle callback); + Handle ComputeLoadCallback(Handle name, + Handle object, + Handle holder, + const CallOptimization& call_optimization); + Handle ComputeLoadViaGetter(Handle name, Handle object, Handle holder, @@ -173,6 +180,12 @@ class StubCache { Handle holder, Handle callback); + Handle ComputeKeyedLoadCallback( + Handle name, + Handle object, + Handle holder, + const CallOptimization& call_optimization); + Handle ComputeKeyedLoadConstant(Handle name, Handle object, Handle holder, @@ -705,6 +718,11 @@ class BaseLoadStubCompiler: public BaseLoadStoreStubCompiler { Handle name, Handle callback); + Handle CompileLoadCallback(Handle object, + Handle holder, + Handle name, + const CallOptimization& call_optimization); + Handle CompileLoadConstant(Handle object, Handle holder, Handle name, @@ -730,7 +748,7 @@ class BaseLoadStubCompiler: public BaseLoadStoreStubCompiler { Handle holder, Handle name, Label* success, - Handle callback); + Handle callback); void NonexistentHandlerFrontend(Handle object, Handle last, Handle name, @@ -744,6 +762,7 @@ class BaseLoadStubCompiler: public BaseLoadStoreStubCompiler { void GenerateLoadConstant(Handle value); void GenerateLoadCallback(Register reg, Handle callback); + void GenerateLoadCallback(const CallOptimization& call_optimization); void GenerateLoadInterceptor(Register holder_reg, Handle object, Handle holder, @@ -1028,8 +1047,6 @@ class KeyedStoreStubCompiler: public BaseStoreStubCompiler { V(ArrayCode) -class CallOptimization; - class CallStubCompiler: public StubCompiler { public: CallStubCompiler(Isolate* isolate, @@ -1161,6 +1178,12 @@ class CallOptimization BASE_EMBEDDED { int GetPrototypeDepthOfExpectedType(Handle object, Handle holder) const; + bool IsCompatibleReceiver(Object* receiver) { + ASSERT(is_simple_api_call()); + if (expected_receiver_type_.is_null()) return true; + return receiver->IsInstanceOf(*expected_receiver_type_); + } + private: void Initialize(Handle function); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 55e4a9b..7e8e511 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -1191,7 +1191,7 @@ Register BaseLoadStubCompiler::CallbackHandlerFrontend( Handle holder, Handle name, Label* success, - Handle callback) { + Handle callback) { Label miss; Register reg = HandlerFrontendHeader(object, object_reg, holder, name, &miss); @@ -1276,6 +1276,29 @@ void BaseLoadStubCompiler::GenerateLoadField(Register reg, void BaseLoadStubCompiler::GenerateLoadCallback( + const CallOptimization& call_optimization) { + ASSERT(call_optimization.is_simple_api_call()); + + // Copy return value. + __ movq(scratch3(), StackSpaceOperand(0)); + // Assign stack space for the call arguments. + __ subq(rsp, Immediate((kFastApiCallArguments + 1) * kPointerSize)); + // Move the return address on top of the stack. + __ movq(StackOperandForReturnAddress(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 - (argc + 6)), receiver()); + + GenerateFastApiCall(masm(), call_optimization, argc); +} + + +void BaseLoadStubCompiler::GenerateLoadCallback( Register reg, Handle callback) { // Insert additional parameters into the stack frame above return address. diff --git a/test/cctest/test-accessors.cc b/test/cctest/test-accessors.cc index b68b0b0..09b0644 100644 --- a/test/cctest/test-accessors.cc +++ b/test/cctest/test-accessors.cc @@ -163,6 +163,7 @@ static void XGetter(Local name, static void XGetter(const v8::FunctionCallbackInfo& info) { + CHECK_EQ(x_receiver, info.Holder()); XGetter(info, 1); } @@ -172,6 +173,7 @@ static void XSetter(Local value, const Info& info, int offset) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); CHECK_EQ(isolate, info.GetIsolate()); CHECK_EQ(x_holder, info.This()); + CHECK_EQ(x_holder, info.Holder()); x_register[offset] = value->Int32Value(); } @@ -179,7 +181,6 @@ static void XSetter(Local value, const Info& info, int offset) { static void XSetter(Local name, Local value, const v8::PropertyCallbackInfo& info) { - CHECK_EQ(x_holder, info.Holder()); XSetter(value, info, 0); } @@ -205,17 +206,23 @@ THREADED_TEST(AccessorIC) { v8::Handle array = v8::Handle::Cast(CompileRun( "obj.__proto__ = holder;" "var result = [];" + "var key_0 = 'x0';" + "var key_1 = 'x1';" "for (var i = 0; i < 10; i++) {" " holder.x0 = i;" - " holder.x1 = i;" " result.push(obj.x0);" + " holder.x1 = i;" " result.push(obj.x1);" + " holder[key_0] = i;" + " result.push(obj[key_0]);" + " holder[key_1] = i;" + " result.push(obj[key_1]);" "}" "result")); - CHECK_EQ(20, array->Length()); - for (int i = 0; i < 20; i++) { + CHECK_EQ(40, array->Length()); + for (int i = 0; i < 40; i++) { v8::Handle entry = array->Get(v8::Integer::New(i)); - CHECK_EQ(v8::Integer::New(i/2), entry); + CHECK_EQ(v8::Integer::New(i/4), entry); } } diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index c0afb6d..e190b71 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -141,10 +141,13 @@ static void ExpectUndefined(const char* code) { static int signature_callback_count; +static Local signature_expected_receiver; static void IncrementingSignatureCallback( const v8::FunctionCallbackInfo& args) { ApiTestFuzzer::Fuzz(); signature_callback_count++; + CHECK_EQ(signature_expected_receiver, args.Holder()); + CHECK_EQ(signature_expected_receiver, args.This()); v8::Handle result = v8::Array::New(args.Length()); for (int i = 0; i < args.Length(); i++) result->Set(v8::Integer::New(i), args[i]); @@ -213,26 +216,50 @@ THREADED_TEST(ReceiverSignature) { 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 = new Fun();" - "o.m();"); - CHECK_EQ(1, signature_callback_count); + "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 sub_fun = v8::FunctionTemplate::New(); sub_fun->Inherit(fun); - env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); + fun_instance = sub_fun->InstanceTemplate()->NewInstance(); + env->Global()->Set(v8_str("fun_instance"), fun_instance); + signature_expected_receiver = fun_instance; CompileRun( - "var o = new SubFun();" - "o.m();"); - CHECK_EQ(2, signature_callback_count); - + "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(2, signature_callback_count); + 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(); @@ -242,7 +269,14 @@ THREADED_TEST(ReceiverSignature) { "var o = new UnrelFun();" "o.m = Fun.prototype.m;" "o.m();"); - CHECK_EQ(2, signature_callback_count); + 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()); } -- 2.7.4