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
This commit is contained in:
dcarney@chromium.org 2013-09-06 11:31:25 +00:00
parent 63c817cc55
commit 3a3a27b2ee
7 changed files with 273 additions and 110 deletions

View File

@ -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<Object> 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<Code> StoreStubCompiler::CompileStoreCallback(
}
Handle<Code> StoreStubCompiler::CompileStoreCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<Name> 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)

View File

@ -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<Code> StoreStubCompiler::CompileStoreCallback(
}
Handle<Code> StoreStubCompiler::CompileStoreCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<Name> 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)

View File

@ -1831,6 +1831,13 @@ Handle<Code> StoreIC::ComputeStoreMonomorphic(LookupResult* lookup,
if (!setter->IsJSFunction()) break;
if (holder->IsGlobalObject()) break;
if (!holder->HasFastProperties()) break;
Handle<JSFunction> function = Handle<JSFunction>::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<JSFunction>::cast(setter),
strict_mode);

View File

@ -631,6 +631,24 @@ Handle<Code> StubCache::ComputeStoreCallback(
}
Handle<Code> StubCache::ComputeStoreCallback(
Handle<Name> name,
Handle<JSObject> receiver,
Handle<JSObject> holder,
const CallOptimization& call_optimization,
StrictModeFlag strict_mode) {
Handle<Code> stub = FindStoreHandler(
name, receiver, Code::STORE_IC, Code::CALLBACKS, strict_mode);
if (!stub.is_null()) return stub;
StoreStubCompiler compiler(isolate_, strict_mode);
Handle<Code> handler = compiler.CompileStoreCallback(
receiver, holder, name, call_optimization);
JSObject::UpdateMapCodeCache(receiver, name, handler);
return handler;
}
Handle<Code> StubCache::ComputeStoreViaSetter(Handle<Name> name,
Handle<JSObject> receiver,
Handle<JSObject> holder,

View File

@ -222,6 +222,12 @@ class StubCache {
Handle<ExecutableAccessorInfo> callback,
StrictModeFlag strict_mode);
Handle<Code> ComputeStoreCallback(Handle<Name> name,
Handle<JSObject> object,
Handle<JSObject> holder,
const CallOptimization& call_optimation,
StrictModeFlag strict_mode);
Handle<Code> ComputeStoreViaSetter(Handle<Name> name,
Handle<JSObject> object,
Handle<JSObject> holder,
@ -960,6 +966,11 @@ class StoreStubCompiler: public BaseStoreStubCompiler {
Handle<Name> name,
Handle<ExecutableAccessorInfo> callback);
Handle<Code> CompileStoreCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Handle<Name> name,
const CallOptimization& call_optimization);
static void GenerateStoreViaSetter(MacroAssembler* masm,
Handle<JSFunction> setter);

View File

@ -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<Code> StoreStubCompiler::CompileStoreCallback(
}
Handle<Code> StoreStubCompiler::CompileStoreCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<Name> 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)

View File

@ -224,78 +224,98 @@ THREADED_TEST(IsolateOfContext) {
}
static void TestSignature(const char* loop_js, Local<Value> receiver) {
i::ScopedVector<char> 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<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
fun->PrototypeTemplate()->Set(
v8_str("m"),
v8::FunctionTemplate::New(IncrementingSignatureCallback,
v8::Handle<Value>(),
sig));
fun->PrototypeTemplate()->SetAccessorProperty(
v8_str("n"),
v8::FunctionTemplate::New(IncrementingSignatureCallback,
v8::Handle<Value>(),
sig));
env->Global()->Set(v8_str("Fun"), fun->GetFunction());
Local<Value> 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<v8::FunctionTemplate> callback_sig =
v8::FunctionTemplate::New(
IncrementingSignatureCallback, Local<Value>(), sig);
v8::Handle<v8::FunctionTemplate> callback =
v8::FunctionTemplate::New(IncrementingSignatureCallback);
v8::Handle<v8::FunctionTemplate> 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<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
sub_fun->Inherit(fun);
// Install properties.
v8::Handle<v8::ObjectTemplate> 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<Value> fun_instance = fun->InstanceTemplate()->NewInstance();
Local<Value> 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<char> source(200);
i::OS::SNPrintF(
source, "var test_object = %s; test_object", test_objects[i]);
Local<Value> 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<Value>();
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);
}
}