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:
parent
63c817cc55
commit
3a3a27b2ee
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user