Commit 856135af authored by ager@chromium.org's avatar ager@chromium.org

Implement fast calls of functions in the presence of eval (if the eval

calls do not introduce new bindings).

The infrastructure is already in place for fast loads from context
slots in the presence of eval.  This change simply uses that
infrastructure for calls as well as loads.

Review URL: http://codereview.chromium.org/2027002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4609 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 89a7341d
......@@ -2787,7 +2787,8 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
frame_->SpillAll();
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
__ ldr(r0,
ContextSlotOperandCheckExtensions(potential_slot,
......@@ -3703,9 +3704,56 @@ void CodeGenerator::VisitCall(Call* node) {
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
// JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
// JavaScript examples:
//
// with (obj) foo(1, 2, 3) // foo is in obj
//
// function f() {};
// function g() {
// eval(...);
// f(); // f could be in extension object
// }
// ----------------------------------
// JumpTargets do not yet support merging frames so the frame must be
// spilled when jumping to these targets.
JumpTarget slow;
JumpTarget done;
// Generate fast-case code for variables that might be shadowed by
// eval-introduced variables. Eval is used a lot without
// introducing variables. In those cases, we do not want to
// perform a runtime call for all variables in the scope
// containing the eval.
if (var->mode() == Variable::DYNAMIC_GLOBAL) {
LoadFromGlobalSlotCheckExtensions(var->slot(), NOT_INSIDE_TYPEOF, &slow);
frame_->EmitPush(r0);
LoadGlobalReceiver(r1);
done.Jump();
} else if (var->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = var->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
__ ldr(r0,
ContextSlotOperandCheckExtensions(potential_slot,
r1,
r2,
&slow));
if (potential_slot->var()->mode() == Variable::CONST) {
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r0, ip);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
}
frame_->EmitPush(r0);
LoadGlobalReceiver(r1);
done.Jump();
}
}
slow.Bind();
// Load the function
frame_->EmitPush(cp);
__ mov(r0, Operand(var->name()));
......@@ -3717,7 +3765,9 @@ void CodeGenerator::VisitCall(Call* node) {
frame_->EmitPush(r0); // function
frame_->EmitPush(r1); // receiver
// Call the function.
done.Bind();
// Call the function. At this point, everything is spilled but the
// function and receiver are in r0 and r1.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
frame_->EmitPush(r0);
......
......@@ -4748,7 +4748,8 @@ Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
} else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
......@@ -5772,11 +5773,66 @@ void CodeGenerator::VisitCall(Call* node) {
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
// JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
// JavaScript examples:
//
// with (obj) foo(1, 2, 3) // foo is in obj
//
// function f() {};
// function g() {
// eval(...);
// f(); // f could be in extension object
// }
// ----------------------------------
// Load the function from the context. Sync the frame so we can
// push the arguments directly into place.
JumpTarget slow;
JumpTarget done;
// Generate fast-case code for variables that might be shadowed by
// eval-introduced variables. Eval is used a lot without
// introducing variables. In those cases, we do not want to
// perform a runtime call for all variables in the scope
// containing the eval.
Result function;
if (var->mode() == Variable::DYNAMIC_GLOBAL) {
function = LoadFromGlobalSlotCheckExtensions(var->slot(),
NOT_INSIDE_TYPEOF,
&slow);
frame_->Push(&function);
LoadGlobalReceiver();
done.Jump();
} else if (var->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = var->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
// value.
function = allocator()->Allocate();
ASSERT(function.is_valid());
__ mov(function.reg(),
ContextSlotOperandCheckExtensions(potential_slot,
function,
&slow));
JumpTarget push_function_and_receiver;
if (potential_slot->var()->mode() == Variable::CONST) {
__ cmp(function.reg(), Factory::the_hole_value());
push_function_and_receiver.Branch(not_equal, &function);
__ mov(function.reg(), Factory::undefined_value());
}
push_function_and_receiver.Bind(&function);
frame_->Push(&function);
LoadGlobalReceiver();
done.Jump();
}
}
slow.Bind();
// Enter the runtime system to load the function from the context.
// Sync the frame so we can push the arguments directly into
// place.
frame_->SyncRange(0, frame_->element_count() - 1);
frame_->EmitPush(esi);
frame_->EmitPush(Immediate(var->name()));
......@@ -5793,6 +5849,7 @@ void CodeGenerator::VisitCall(Call* node) {
ASSERT(!allocator()->is_used(edx));
frame_->EmitPush(edx);
done.Bind();
// Call the function.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
......
......@@ -2866,9 +2866,63 @@ void CodeGenerator::VisitCall(Call* node) {
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
// JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
// JavaScript examples:
//
// with (obj) foo(1, 2, 3) // foo is in obj
//
// function f() {};
// function g() {
// eval(...);
// f(); // f could be in extension object
// }
// ----------------------------------
JumpTarget slow;
JumpTarget done;
// Generate fast-case code for variables that might be shadowed by
// eval-introduced variables. Eval is used a lot without
// introducing variables. In those cases, we do not want to
// perform a runtime call for all variables in the scope
// containing the eval.
Result function;
if (var->mode() == Variable::DYNAMIC_GLOBAL) {
function = LoadFromGlobalSlotCheckExtensions(var->slot(),
NOT_INSIDE_TYPEOF,
&slow);
frame_->Push(&function);
LoadGlobalReceiver();
done.Jump();
} else if (var->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = var->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
// value.
function = allocator()->Allocate();
ASSERT(function.is_valid());
__ movq(function.reg(),
ContextSlotOperandCheckExtensions(potential_slot,
function,
&slow));
JumpTarget push_function_and_receiver;
if (potential_slot->var()->mode() == Variable::CONST) {
__ CompareRoot(function.reg(), Heap::kTheHoleValueRootIndex);
push_function_and_receiver.Branch(not_equal, &function);
__ LoadRoot(function.reg(), Heap::kUndefinedValueRootIndex);
}
push_function_and_receiver.Bind(&function);
frame_->Push(&function);
LoadGlobalReceiver();
done.Jump();
}
}
slow.Bind();
// Load the function from the context. Sync the frame so we can
// push the arguments directly into place.
frame_->SyncRange(0, frame_->element_count() - 1);
......@@ -2887,6 +2941,7 @@ void CodeGenerator::VisitCall(Call* node) {
ASSERT(!allocator()->is_used(rdx));
frame_->EmitPush(rdx);
done.Bind();
// Call the function.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
......@@ -5178,7 +5233,8 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
} else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
// This rules out argument loads.
// This rules out argument loads because eval forces arguments
// access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
......
......@@ -28,17 +28,55 @@
// Tests loading of properties across eval calls.
var x = 1;
function global_function() { return 'global'; }
const const_uninitialized;
const const_initialized = function() { return "const_global"; }
// Test loading across an eval call that does not shadow variables.
function testNoShadowing() {
var y = 2;
function local_function() { return 'local'; }
const local_const_uninitialized;
const local_const_initialized = function() { return "const_local"; }
function f() {
eval('1');
assertEquals(1, x);
assertEquals(2, y);
assertEquals('global', global_function());
assertEquals('local', local_function());
try {
const_uninitialized();
assertUnreachable();
} catch(e) {
// Ignore.
}
assertEquals('const_global', const_initialized());
try {
local_const_uninitialized();
assertUnreachable();
} catch(e) {
// Ignore.
}
assertEquals('const_local', local_const_initialized());
function g() {
assertEquals(1, x);
assertEquals(2, y);
assertEquals('global', global_function());
assertEquals('local', local_function());
try {
const_uninitialized();
assertUnreachable();
} catch(e) {
// Ignore.
}
assertEquals('const_global', const_initialized());
try {
local_const_uninitialized();
assertUnreachable();
} catch(e) {
// Ignore.
}
assertEquals('const_local', local_const_initialized());
}
g();
}
......@@ -50,14 +88,19 @@ testNoShadowing();
// Test loading across eval calls that do not shadow variables.
function testNoShadowing2() {
var y = 2;
function local_function() { return 'local'; }
eval('1');
function f() {
eval('1');
assertEquals(1, x);
assertEquals(2, y);
assertEquals('global', global_function());
assertEquals('local', local_function());
function g() {
assertEquals(1, x);
assertEquals(2, y);
assertEquals('global', global_function());
assertEquals('local', local_function());
}
g();
}
......@@ -69,13 +112,20 @@ testNoShadowing2();
// Test loading across an eval call that shadows variables.
function testShadowing() {
var y = 2;
function local_function() { return 'local'; }
function f() {
eval('var x = 3; var y = 4;');
assertEquals(3, x);
assertEquals(4, y);
eval('function local_function() { return "new_local"; }');
eval('function global_function() { return "new_nonglobal"; }');
assertEquals('new_nonglobal', global_function());
assertEquals('new_local', local_function());
function g() {
assertEquals(3, x);
assertEquals(4, y);
assertEquals('new_nonglobal', global_function());
assertEquals('new_local', local_function());
}
g();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment