Make the ResolvePossiblyDirectEval faster by avoiding the

stack traversal code.
Review URL: http://codereview.chromium.org/523051

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3533 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kasperl@chromium.org 2010-01-05 09:38:02 +00:00
parent b74daccff0
commit 15fe7a8ad6
9 changed files with 103 additions and 108 deletions

View File

@ -2892,13 +2892,15 @@ void CodeGenerator::VisitCall(Call* node) {
frame_->EmitPush(r2);
}
// Push the receiver.
__ ldr(r1, frame_->Receiver());
frame_->EmitPush(r1);
// Resolve the call.
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// Touch up stack with the right values for the function and the receiver.
__ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize));
__ str(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize + kPointerSize));
__ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ str(r1, MemOperand(sp, arg_count * kPointerSize));
// Call the function.

View File

@ -992,6 +992,7 @@ void Genesis::InstallNativeFunctions() {
INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun);
INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun);
INSTALL_NATIVE(JSFunction, "ToBoolean", to_boolean_fun);
INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun);
INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun);
INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance",
configure_instance_fun);

View File

@ -77,6 +77,7 @@ enum ContextLookupFlags {
V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
V(FUNCTION_MAP_INDEX, Map, function_map) \
@ -202,6 +203,7 @@ class Context: public FixedArray {
TO_UINT32_FUN_INDEX,
TO_INT32_FUN_INDEX,
TO_BOOLEAN_FUN_INDEX,
GLOBAL_EVAL_FUN_INDEX,
INSTANTIATE_FUN_INDEX,
CONFIGURE_INSTANCE_FUN_INDEX,
SPECIAL_FUNCTION_TABLE_INDEX,

View File

@ -4580,22 +4580,19 @@ void CodeGenerator::VisitCall(Call* node) {
frame_->Push(Factory::undefined_value());
}
// Push the receiver.
frame_->PushParameterAt(-1);
// Resolve the call.
Result result =
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// Touch up the stack with the right values for the function and the
// receiver. Use a scratch register to avoid destroying the result.
Result scratch = allocator_->Allocate();
ASSERT(scratch.is_valid());
__ mov(scratch.reg(), FieldOperand(result.reg(), FixedArray::kHeaderSize));
frame_->SetElementAt(arg_count + 1, &scratch);
// We can reuse the result register now.
frame_->Spill(result.reg());
__ mov(result.reg(),
FieldOperand(result.reg(), FixedArray::kHeaderSize + kPointerSize));
frame_->SetElementAt(arg_count, &result);
// The runtime call returns a pair of values in eax (function) and
// edx (receiver). Touch up the stack with the right values.
Result receiver = allocator_->Allocate(edx);
frame_->SetElementAt(arg_count + 1, &result);
frame_->SetElementAt(arg_count, &receiver);
receiver.Unuse();
// Call the function.
CodeForSourcePosition(node->position());

View File

@ -5233,51 +5233,31 @@ static Object* Runtime_CompileString(Arguments args) {
}
static Handle<JSFunction> GetBuiltinFunction(String* name) {
LookupResult result;
Top::global_context()->builtins()->LocalLookup(name, &result);
return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
}
static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
ASSERT(args.length() == 3);
if (!args[0]->IsJSFunction()) {
return MakePair(Top::ThrowIllegalOperation(), NULL);
}
static Object* CompileDirectEval(Handle<String> source) {
// Compute the eval context.
HandleScope scope;
Handle<JSFunction> callee = args.at<JSFunction>(0);
Handle<Object> receiver; // Will be overwritten.
// Compute the calling context.
Handle<Context> context = Handle<Context>(Top::context());
#ifdef DEBUG
// Make sure Top::context() agrees with the old code that traversed
// the stack frames to compute the context.
StackFrameLocator locator;
JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
Handle<Context> context(Context::cast(frame->context()));
bool is_global = context->IsGlobalContext();
// Compile source string in the current context.
Handle<JSFunction> boilerplate = Compiler::CompileEval(
source,
context,
is_global,
Compiler::DONT_VALIDATE_JSON);
if (boilerplate.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
Factory::NewFunctionFromBoilerplate(boilerplate, context, NOT_TENURED);
return *fun;
}
static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
ASSERT(args.length() == 2);
HandleScope scope;
CONVERT_ARG_CHECKED(JSFunction, callee, 0);
Handle<Object> receiver;
ASSERT(Context::cast(frame->context()) == *context);
#endif
// Find where the 'eval' symbol is bound. It is unaliased only if
// it is bound in the global context.
StackFrameLocator locator;
JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
Handle<Context> context(Context::cast(frame->context()));
int index;
PropertyAttributes attributes;
while (!context.is_null()) {
int index = -1;
PropertyAttributes attributes = ABSENT;
while (true) {
receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
&index, &attributes);
// Stop search when eval is found or when the global context is
@ -5296,46 +5276,42 @@ static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
Handle<Object> name = Factory::eval_symbol();
Handle<Object> reference_error =
Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
return Top::Throw(*reference_error);
return MakePair(Top::Throw(*reference_error), NULL);
}
if (context->IsGlobalContext()) {
// 'eval' is bound in the global context, but it may have been overwritten.
// Compare it to the builtin 'GlobalEval' function to make sure.
Handle<JSFunction> global_eval =
GetBuiltinFunction(Heap::global_eval_symbol());
if (global_eval.is_identical_to(callee)) {
// A direct eval call.
if (args[1]->IsString()) {
CONVERT_ARG_CHECKED(String, source, 1);
// A normal eval call on a string. Compile it and return the
// compiled function bound in the local context.
Object* compiled_source = CompileDirectEval(source);
if (compiled_source->IsFailure()) return compiled_source;
receiver = Handle<Object>(frame->receiver());
callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
} else {
// An eval call that is not called on a string. Global eval
// deals better with this.
receiver = Handle<Object>(Top::global_context()->global());
}
} else {
// 'eval' is overwritten. Just call the function with the given arguments.
receiver = Handle<Object>(Top::global_context()->global());
}
} else {
if (!context->IsGlobalContext()) {
// 'eval' is not bound in the global context. Just call the function
// with the given arguments. This is not necessarily the global eval.
if (receiver->IsContext()) {
context = Handle<Context>::cast(receiver);
receiver = Handle<Object>(context->get(index));
} else if (receiver->IsJSContextExtensionObject()) {
receiver = Handle<JSObject>(Top::context()->global()->global_receiver());
}
return MakePair(*callee, *receiver);
}
Handle<FixedArray> call = Factory::NewFixedArray(2);
call->set(0, *callee);
call->set(1, *receiver);
return *call;
// 'eval' is bound in the global context, but it may have been overwritten.
// Compare it to the builtin 'GlobalEval' function to make sure.
if (*callee != Top::global_context()->global_eval_fun() ||
!args[1]->IsString()) {
return MakePair(*callee, Top::context()->global()->global_receiver());
}
// Deal with a normal eval call with a string argument. Compile it
// and return the compiled function bound in the local context.
Handle<String> source = args.at<String>(1);
Handle<JSFunction> boilerplate = Compiler::CompileEval(
source,
Handle<Context>(Top::context()),
Top::context()->IsGlobalContext(),
Compiler::DONT_VALIDATE_JSON);
if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
callee = Factory::NewFunctionFromBoilerplate(
boilerplate,
Handle<Context>(Top::context()),
NOT_TENURED);
return MakePair(*callee, args[2]);
}

View File

@ -202,7 +202,7 @@ namespace internal {
\
/* Eval */ \
F(GlobalReceiver, 1, 1) \
F(ResolvePossiblyDirectEval, 2, 1) \
F(ResolvePossiblyDirectEval, 3, 1) \
\
F(SetProperty, -1 /* 3 or 4 */, 1) \
F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */, 1) \

View File

@ -236,7 +236,7 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) {
Variable* Scope::DeclareGlobal(Handle<String> name) {
ASSERT(is_global_scope());
return variables_.Declare(this, name, Variable::DYNAMIC, true,
return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, true,
Variable::NORMAL);
}

View File

@ -2671,23 +2671,19 @@ void CodeGenerator::VisitCall(Call* node) {
frame_->Push(Factory::undefined_value());
}
// Push the receiver.
frame_->PushParameterAt(-1);
// Resolve the call.
Result result =
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
// Touch up the stack with the right values for the function and the
// receiver. Use a scratch register to avoid destroying the result.
Result scratch = allocator_->Allocate();
ASSERT(scratch.is_valid());
__ movq(scratch.reg(),
FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(0)));
frame_->SetElementAt(arg_count + 1, &scratch);
// We can reuse the result register now.
frame_->Spill(result.reg());
__ movq(result.reg(),
FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(1)));
frame_->SetElementAt(arg_count, &result);
// The runtime call returns a pair of values in rax (function) and
// rdx (receiver). Touch up the stack with the right values.
Result receiver = allocator_->Allocate(rdx);
frame_->SetElementAt(arg_count + 1, &result);
frame_->SetElementAt(arg_count, &receiver);
receiver.Unuse();
// Call the function.
CodeForSourcePosition(node->position());

View File

@ -58,16 +58,16 @@ eval = global_eval;
// Test that un-aliased eval reads from local context.
foo = 0;
result =
result =
(function() {
var foo = 2;
return eval('foo');
})();
assertEquals(2, result);
//Test that un-aliased eval writes to local context.
// Test that un-aliased eval writes to local context.
foo = 0;
result =
result =
(function() {
var foo = 1;
eval('foo = 2');
@ -84,7 +84,7 @@ assertTrue(o === o.self);
// Test that aliased eval reads from global context.
var e = eval;
foo = 0;
result =
result =
(function() {
var foo = 2;
return e('foo');
@ -105,7 +105,7 @@ assertTrue(this === o.self);
// Try to cheat the 'aliased eval' detection.
var x = this;
foo = 0;
result =
result =
(function() {
var foo = 2;
return x.eval('foo');
@ -113,7 +113,7 @@ result =
assertEquals(0, result);
foo = 0;
result =
result =
(function() {
var eval = function(x) { return x; };
var foo = eval(2);
@ -128,8 +128,29 @@ result =
})();
assertEquals(4, result);
result =
(function() {
eval("var eval = function(s) { return this; }");
return eval("42"); // Should return the global object
})();
assertEquals(this, result);
result =
(function() {
var obj = { f: function(eval) { return eval("this"); } };
return obj.f(eval);
})();
assertEquals(this, result);
result =
(function() {
var obj = { f: function(eval) { arguments; return eval("this"); } };
return obj.f(eval);
})();
assertEquals(this, result);
eval = function(x) { return 2 * x; };
result =
result =
(function() {
return (function() { return eval(2); })();
})();