Allocate arguments object on-demand instead of at function entry.

This allows Function.prototype.apply to not allocate the objects 
and copy the arguments directly from the stack.
Review URL: http://codereview.chromium.org/147075

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2256 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kasperl@chromium.org 2009-06-24 08:01:38 +00:00
parent 69764a5d2a
commit f66ea38c0b
14 changed files with 558 additions and 76 deletions

View File

@ -511,7 +511,10 @@ Object* Accessors::FunctionGetArguments(Object* object, void*) {
// If there is an arguments variable in the stack, we return that. // If there is an arguments variable in the stack, we return that.
int index = ScopeInfo<>::StackSlotIndex(frame->code(), int index = ScopeInfo<>::StackSlotIndex(frame->code(),
Heap::arguments_symbol()); Heap::arguments_symbol());
if (index >= 0) return frame->GetExpression(index); if (index >= 0) {
Handle<Object> arguments = Handle<Object>(frame->GetExpression(index));
if (!arguments->IsTheHole()) return *arguments;
}
// If there isn't an arguments variable in the stack, we need to // If there isn't an arguments variable in the stack, we need to
// find the frame that holds the actual arguments passed to the // find the frame that holds the actual arguments passed to the

View File

@ -68,7 +68,7 @@ VariableProxy::VariableProxy(Handle<String> name,
// names must be canonicalized for fast equality checks // names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol()); ASSERT(name->IsSymbol());
// at least one access, otherwise no need for a VariableProxy // at least one access, otherwise no need for a VariableProxy
var_uses_.RecordAccess(1); var_uses_.RecordRead(1);
} }

View File

@ -802,13 +802,20 @@ class VariableProxy: public Expression {
Variable* AsVariable() { Variable* AsVariable() {
return this == NULL || var_ == NULL ? NULL : var_->AsVariable(); return this == NULL || var_ == NULL ? NULL : var_->AsVariable();
} }
virtual bool IsValidLeftHandSide() { virtual bool IsValidLeftHandSide() {
return var_ == NULL ? true : var_->IsValidLeftHandSide(); return var_ == NULL ? true : var_->IsValidLeftHandSide();
} }
bool IsVariable(Handle<String> n) { bool IsVariable(Handle<String> n) {
return !is_this() && name().is_identical_to(n); return !is_this() && name().is_identical_to(n);
} }
bool IsArguments() {
Variable* variable = AsVariable();
return (variable == NULL) ? false : variable->is_arguments();
}
// If this assertion fails it means that some code has tried to // If this assertion fails it means that some code has tried to
// treat the special "this" variable as an ordinary variable with // treat the special "this" variable as an ordinary variable with
// the name "this". // the name "this".
@ -890,12 +897,13 @@ class Slot: public Expression {
virtual void Accept(AstVisitor* v); virtual void Accept(AstVisitor* v);
// Type testing & conversion // Type testing & conversion
virtual Slot* AsSlot() { return this; } virtual Slot* AsSlot() { return this; }
// Accessors // Accessors
Variable* var() const { return var_; } Variable* var() const { return var_; }
Type type() const { return type_; } Type type() const { return type_; }
int index() const { return index_; } int index() const { return index_; }
bool is_arguments() const { return var_->is_arguments(); }
private: private:
Variable* var_; Variable* var_;

View File

@ -175,18 +175,7 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
function_return_.set_direction(JumpTarget::BIDIRECTIONAL); function_return_.set_direction(JumpTarget::BIDIRECTIONAL);
function_return_is_shadowed_ = false; function_return_is_shadowed_ = false;
// Allocate the arguments object and copy the parameters into it. // Allocate the local context if needed.
if (scope_->arguments() != NULL) {
ASSERT(scope_->arguments_shadow() != NULL);
Comment cmnt(masm_, "[ Allocate arguments object");
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
frame_->PushFunction();
frame_->PushReceiverSlotAddress();
frame_->Push(Smi::FromInt(scope_->num_parameters()));
Result answer = frame_->CallStub(&stub, 3);
frame_->Push(&answer);
}
if (scope_->num_heap_slots() > 0) { if (scope_->num_heap_slots() > 0) {
Comment cmnt(masm_, "[ allocate local context"); Comment cmnt(masm_, "[ allocate local context");
// Allocate local context. // Allocate local context.
@ -247,27 +236,11 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
} }
} }
// This section stores the pointer to the arguments object that
// was allocated and copied into above. If the address was not
// saved to TOS, we push ecx onto the stack.
//
// Store the arguments object. This must happen after context // Store the arguments object. This must happen after context
// initialization because the arguments object may be stored in the // initialization because the arguments object may be stored in
// context. // the context.
if (scope_->arguments() != NULL) { if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) {
Comment cmnt(masm_, "[ store arguments object"); StoreArgumentsObject(true);
{ Reference shadow_ref(this, scope_->arguments_shadow());
ASSERT(shadow_ref.is_slot());
{ Reference arguments_ref(this, scope_->arguments());
ASSERT(arguments_ref.is_slot());
// Here we rely on the convenient property that references to slot
// take up zero space in the frame (ie, it doesn't matter that the
// stored value is actually below the reference on the frame).
arguments_ref.SetValue(NOT_CONST_INIT);
}
shadow_ref.SetValue(NOT_CONST_INIT);
}
frame_->Drop(); // Value is no longer needed.
} }
// Generate code to 'execute' declarations and initialize functions // Generate code to 'execute' declarations and initialize functions
@ -591,6 +564,71 @@ void CodeGenerator::LoadTypeofExpression(Expression* x) {
} }
ArgumentsAllocationMode CodeGenerator::ArgumentsMode() const {
if (scope_->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION;
ASSERT(scope_->arguments_shadow() != NULL);
// We don't want to do lazy arguments allocation for functions that
// have heap-allocated contexts, because it interfers with the
// uninitialized const tracking in the context objects.
return (scope_->num_heap_slots() > 0)
? EAGER_ARGUMENTS_ALLOCATION
: LAZY_ARGUMENTS_ALLOCATION;
}
Result CodeGenerator::StoreArgumentsObject(bool initial) {
ArgumentsAllocationMode mode = ArgumentsMode();
ASSERT(mode != NO_ARGUMENTS_ALLOCATION);
Comment cmnt(masm_, "[ store arguments object");
if (mode == LAZY_ARGUMENTS_ALLOCATION && initial) {
// When using lazy arguments allocation, we store the hole value
// as a sentinel indicating that the arguments object hasn't been
// allocated yet.
frame_->Push(Factory::the_hole_value());
} else {
ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
frame_->PushFunction();
frame_->PushReceiverSlotAddress();
frame_->Push(Smi::FromInt(scope_->num_parameters()));
Result result = frame_->CallStub(&stub, 3);
frame_->Push(&result);
}
{ Reference shadow_ref(this, scope_->arguments_shadow());
Reference arguments_ref(this, scope_->arguments());
ASSERT(shadow_ref.is_slot() && arguments_ref.is_slot());
// Here we rely on the convenient property that references to slot
// take up zero space in the frame (ie, it doesn't matter that the
// stored value is actually below the reference on the frame).
JumpTarget done;
bool skip_arguments = false;
if (mode == LAZY_ARGUMENTS_ALLOCATION && !initial) {
// We have to skip storing into the arguments slot if it has
// already been written to. This can happen if the a function
// has a local variable named 'arguments'.
LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
Result arguments = frame_->Pop();
if (arguments.is_constant()) {
// We have to skip updating the arguments object if it has
// been assigned a proper value.
skip_arguments = !arguments.handle()->IsTheHole();
} else {
__ cmp(Operand(arguments.reg()), Immediate(Factory::the_hole_value()));
arguments.Unuse();
done.Branch(not_equal);
}
}
if (!skip_arguments) {
arguments_ref.SetValue(NOT_CONST_INIT);
if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind();
}
shadow_ref.SetValue(NOT_CONST_INIT);
}
return frame_->Pop();
}
Reference::Reference(CodeGenerator* cgen, Expression* expression) Reference::Reference(CodeGenerator* cgen, Expression* expression)
: cgen_(cgen), expression_(expression), type_(ILLEGAL) { : cgen_(cgen), expression_(expression), type_(ILLEGAL) {
cgen->LoadReference(this); cgen->LoadReference(this);
@ -2090,6 +2128,172 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
} }
void CodeGenerator::CallApplyLazy(Property* apply,
Expression* receiver,
VariableProxy* arguments,
int position) {
ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION);
ASSERT(arguments->IsArguments());
JumpTarget slow, done;
// Load the apply function onto the stack. This will usually
// give us a megamorphic load site. Not super, but it works.
Reference ref(this, apply);
ref.GetValue(NOT_INSIDE_TYPEOF);
ASSERT(ref.type() == Reference::NAMED);
// Load the receiver and the existing arguments object onto the
// expression stack. Avoid allocating the arguments object here.
Load(receiver);
LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
// Emit the source position information after having loaded the
// receiver and the arguments.
CodeForSourcePosition(position);
// Check if the arguments object has been lazily allocated
// already. If so, just use that instead of copying the arguments
// from the stack. This also deals with cases where a local variable
// named 'arguments' has been introduced.
frame_->Dup();
Result probe = frame_->Pop();
bool try_lazy = true;
if (probe.is_constant()) {
try_lazy = probe.handle()->IsTheHole();
} else {
__ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value()));
probe.Unuse();
slow.Branch(not_equal);
}
if (try_lazy) {
JumpTarget build_args;
// Get rid of the arguments object probe.
frame_->Drop();
// Before messing with the execution stack, we sync all
// elements. This is bound to happen anyway because we're
// about to call a function.
frame_->SyncRange(0, frame_->element_count() - 1);
// Check that the receiver really is a JavaScript object.
{ frame_->PushElementAt(0);
Result receiver = frame_->Pop();
receiver.ToRegister();
__ test(receiver.reg(), Immediate(kSmiTagMask));
build_args.Branch(zero);
Result tmp = allocator_->Allocate();
__ CmpObjectType(receiver.reg(), FIRST_JS_OBJECT_TYPE, tmp.reg());
build_args.Branch(less);
__ cmp(tmp.reg(), LAST_JS_OBJECT_TYPE);
build_args.Branch(greater);
}
// Verify that we're invoking Function.prototype.apply.
{ frame_->PushElementAt(1);
Result apply = frame_->Pop();
apply.ToRegister();
__ test(apply.reg(), Immediate(kSmiTagMask));
build_args.Branch(zero);
Result tmp = allocator_->Allocate();
__ CmpObjectType(apply.reg(), JS_FUNCTION_TYPE, tmp.reg());
build_args.Branch(not_equal);
__ mov(tmp.reg(),
FieldOperand(apply.reg(), JSFunction::kSharedFunctionInfoOffset));
Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply));
__ cmp(FieldOperand(tmp.reg(), SharedFunctionInfo::kCodeOffset),
Immediate(apply_code));
build_args.Branch(not_equal);
}
// Get the function receiver from the stack. Check that it
// really is a function.
__ mov(edi, Operand(esp, 2 * kPointerSize));
__ test(edi, Immediate(kSmiTagMask));
build_args.Branch(zero);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
build_args.Branch(not_equal);
// Copy the arguments to this function possibly from the
// adaptor frame below it.
Label invoke, adapted;
__ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
__ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
__ cmp(ecx, ArgumentsAdaptorFrame::SENTINEL);
__ j(equal, &adapted);
// No arguments adaptor frame. Copy fixed number of arguments.
__ mov(eax, Immediate(scope_->num_parameters()));
for (int i = 0; i < scope_->num_parameters(); i++) {
__ push(frame_->ParameterAt(i));
}
__ jmp(&invoke);
// Arguments adaptor frame present. Copy arguments from there, but
// avoid copying too many arguments to avoid stack overflows.
__ bind(&adapted);
static const uint32_t kArgumentsLimit = 1 * KB;
__ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ shr(eax, kSmiTagSize);
__ mov(ecx, Operand(eax));
__ cmp(eax, kArgumentsLimit);
build_args.Branch(above);
// Loop through the arguments pushing them onto the execution
// stack. We don't inform the virtual frame of the push, so we don't
// have to worry about getting rid of the elements from the virtual
// frame.
Label loop;
__ bind(&loop);
__ test(ecx, Operand(ecx));
__ j(zero, &invoke);
__ push(Operand(edx, ecx, times_4, 1 * kPointerSize));
__ dec(ecx);
__ jmp(&loop);
// Invoke the function. The virtual frame knows about the receiver
// so make sure to forget that explicitly.
__ bind(&invoke);
ParameterCount actual(eax);
__ InvokeFunction(edi, actual, CALL_FUNCTION);
frame_->Forget(1);
Result result = allocator()->Allocate(eax);
frame_->SetElementAt(0, &result);
done.Jump();
// Slow-case: Allocate the arguments object since we know it isn't
// there, and fall-through to the slow-case where we call
// Function.prototype.apply.
build_args.Bind();
Result arguments_object = StoreArgumentsObject(false);
frame_->Push(&arguments_object);
slow.Bind();
}
// Flip the apply function and the function to call on the stack, so
// the function looks like the receiver of the apply call. This way,
// the generic Function.prototype.apply implementation can deal with
// the call like it usually does.
Result a2 = frame_->Pop();
Result a1 = frame_->Pop();
Result ap = frame_->Pop();
Result fn = frame_->Pop();
frame_->Push(&ap);
frame_->Push(&fn);
frame_->Push(&a1);
frame_->Push(&a2);
CallFunctionStub call_function(2, NOT_IN_LOOP);
Result res = frame_->CallStub(&call_function, 3);
frame_->Push(&res);
// All done. Restore context register after call.
if (try_lazy) done.Bind();
frame_->RestoreContextRegister();
}
class DeferredStackCheck: public DeferredCode { class DeferredStackCheck: public DeferredCode {
public: public:
DeferredStackCheck() { DeferredStackCheck() {
@ -3615,6 +3819,44 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
} }
void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
TypeofState state) {
LoadFromSlot(slot, state);
// Bail out quickly if we're not using lazy arguments allocation.
if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
// ... or if the slot isn't a non-parameter arguments slot.
if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
// Pop the loaded value from the stack.
Result value = frame_->Pop();
// If the loaded value is a constant, we know if the arguments
// object has been lazily loaded yet.
if (value.is_constant()) {
if (value.handle()->IsTheHole()) {
Result arguments = StoreArgumentsObject(false);
frame_->Push(&arguments);
} else {
frame_->Push(&value);
}
return;
}
// The loaded value is in a register. If it is the sentinel that
// indicates that we haven't loaded the arguments object yet, we
// need to do it now.
JumpTarget exit;
__ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
frame_->Push(&value);
exit.Branch(not_equal);
Result arguments = StoreArgumentsObject(false);
frame_->SetElementAt(0, &arguments);
exit.Bind();
}
Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
Slot* slot, Slot* slot,
TypeofState typeof_state, TypeofState typeof_state,
@ -3787,7 +4029,7 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
void CodeGenerator::VisitSlot(Slot* node) { void CodeGenerator::VisitSlot(Slot* node) {
Comment cmnt(masm_, "[ Slot"); Comment cmnt(masm_, "[ Slot");
LoadFromSlot(node, typeof_state()); LoadFromSlotCheckForArguments(node, typeof_state());
} }
@ -4349,24 +4591,41 @@ void CodeGenerator::VisitCall(Call* node) {
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)' // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Push the name of the function and the receiver onto the stack. Handle<String> name = Handle<String>::cast(literal->handle());
frame_->Push(literal->handle());
Load(property->obj());
// Load the arguments. if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION &&
int arg_count = args->length(); name->IsEqualTo(CStrVector("apply")) &&
for (int i = 0; i < arg_count; i++) { args->length() == 2 &&
Load(args->at(i)); args->at(1)->AsVariableProxy() != NULL &&
args->at(1)->AsVariableProxy()->IsArguments()) {
// Use the optimized Function.prototype.apply that avoids
// allocating lazily allocated arguments objects.
CallApplyLazy(property,
args->at(0),
args->at(1)->AsVariableProxy(),
node->position());
} else {
// Push the name of the function and the receiver onto the stack.
frame_->Push(name);
Load(property->obj());
// Load the arguments.
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
Load(args->at(i));
}
// Call the IC initialization code.
CodeForSourcePosition(node->position());
Result result =
frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count,
loop_nesting());
frame_->RestoreContextRegister();
// Replace the function on the stack with the result.
frame_->SetElementAt(0, &result);
} }
// Call the IC initialization code.
CodeForSourcePosition(node->position());
Result result =
frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting());
frame_->RestoreContextRegister();
// Replace the function on the stack with the result.
frame_->SetElementAt(0, &result);
} else { } else {
// ------------------------------------------- // -------------------------------------------
// JavaScript example: 'array[index](1, 2, 3)' // JavaScript example: 'array[index](1, 2, 3)'
@ -5838,7 +6097,7 @@ void Reference::GetValue(TypeofState typeof_state) {
Comment cmnt(masm, "[ Load from Slot"); Comment cmnt(masm, "[ Load from Slot");
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL); ASSERT(slot != NULL);
cgen_->LoadFromSlot(slot, typeof_state); cgen_->LoadFromSlotCheckForArguments(slot, typeof_state);
break; break;
} }

View File

@ -273,6 +273,14 @@ class CodeGenState BASE_EMBEDDED {
}; };
// -------------------------------------------------------------------------
// Arguments allocation mode
enum ArgumentsAllocationMode {
NO_ARGUMENTS_ALLOCATION,
EAGER_ARGUMENTS_ALLOCATION,
LAZY_ARGUMENTS_ALLOCATION
};
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -332,12 +340,11 @@ class CodeGenerator: public AstVisitor {
// Accessors // Accessors
Scope* scope() const { return scope_; } Scope* scope() const { return scope_; }
bool is_eval() { return is_eval_; }
// Generating deferred code. // Generating deferred code.
void ProcessDeferred(); void ProcessDeferred();
bool is_eval() { return is_eval_; }
// State // State
TypeofState typeof_state() const { return state_->typeof_state(); } TypeofState typeof_state() const { return state_->typeof_state(); }
ControlDestination* destination() const { return state_->destination(); } ControlDestination* destination() const { return state_->destination(); }
@ -373,6 +380,12 @@ class CodeGenerator: public AstVisitor {
// target (which can not be done more than once). // target (which can not be done more than once).
void GenerateReturnSequence(Result* return_value); void GenerateReturnSequence(Result* return_value);
// Returns the arguments allocation mode.
ArgumentsAllocationMode ArgumentsMode() const;
// Store the arguments object and allocate it if necessary.
Result StoreArgumentsObject(bool initial);
// The following are used by class Reference. // The following are used by class Reference.
void LoadReference(Reference* ref); void LoadReference(Reference* ref);
void UnloadReference(Reference* ref); void UnloadReference(Reference* ref);
@ -408,6 +421,7 @@ class CodeGenerator: public AstVisitor {
// Read a value from a slot and leave it on top of the expression stack. // Read a value from a slot and leave it on top of the expression stack.
void LoadFromSlot(Slot* slot, TypeofState typeof_state); void LoadFromSlot(Slot* slot, TypeofState typeof_state);
void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
Result LoadFromGlobalSlotCheckExtensions(Slot* slot, Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
TypeofState typeof_state, TypeofState typeof_state,
JumpTarget* slow); JumpTarget* slow);
@ -470,6 +484,14 @@ class CodeGenerator: public AstVisitor {
void CallWithArguments(ZoneList<Expression*>* arguments, int position); void CallWithArguments(ZoneList<Expression*>* arguments, int position);
// Use an optimized version of Function.prototype.apply that avoid
// allocating the arguments object and just copies the arguments
// from the stack.
void CallApplyLazy(Property* apply,
Expression* receiver,
VariableProxy* arguments,
int position);
void CheckStack(); void CheckStack();
struct InlineRuntimeLUT { struct InlineRuntimeLUT {

View File

@ -1582,7 +1582,8 @@ VariableProxy* AstBuildingParser::Declare(Handle<String> name,
// For global const variables we bind the proxy to a variable. // For global const variables we bind the proxy to a variable.
if (mode == Variable::CONST && top_scope_->is_global_scope()) { if (mode == Variable::CONST && top_scope_->is_global_scope()) {
ASSERT(resolve); // should be set by all callers ASSERT(resolve); // should be set by all callers
var = NEW(Variable(top_scope_, name, Variable::CONST, true, false)); Variable::Kind kind = Variable::NORMAL;
var = NEW(Variable(top_scope_, name, Variable::CONST, true, kind));
} }
// If requested and we have a local variable, bind the proxy to the variable // If requested and we have a local variable, bind the proxy to the variable

View File

@ -391,8 +391,9 @@ function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
function APPLY_PREPARE(args) { function APPLY_PREPARE(args) {
var length; var length;
// First check whether length is a positive Smi and args is an array. This is the // First check whether length is a positive Smi and args is an
// fast case. If this fails, we do the slow case that takes care of more eventualities // array. This is the fast case. If this fails, we do the slow case
// that takes care of more eventualities.
if (%_IsArray(args)) { if (%_IsArray(args)) {
length = args.length; length = args.length;
if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) { if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {

View File

@ -81,12 +81,12 @@ Variable* LocalsMap::Declare(Scope* scope,
Handle<String> name, Handle<String> name,
Variable::Mode mode, Variable::Mode mode,
bool is_valid_LHS, bool is_valid_LHS,
bool is_this) { Variable::Kind kind) {
HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true); HashMap::Entry* p = HashMap::Lookup(name.location(), name->Hash(), true);
if (p->value == NULL) { if (p->value == NULL) {
// The variable has not been declared yet -> insert it. // The variable has not been declared yet -> insert it.
ASSERT(p->key == name.location()); ASSERT(p->key == name.location());
p->value = new Variable(scope, name, mode, is_valid_LHS, is_this); p->value = new Variable(scope, name, mode, is_valid_LHS, kind);
} }
return reinterpret_cast<Variable*>(p->value); return reinterpret_cast<Variable*>(p->value);
} }
@ -169,7 +169,8 @@ void Scope::Initialize(bool inside_with) {
// such parameter is 'this' which is passed on the stack when // such parameter is 'this' which is passed on the stack when
// invoking scripts // invoking scripts
{ Variable* var = { Variable* var =
locals_.Declare(this, Factory::this_symbol(), Variable::VAR, false, true); locals_.Declare(this, Factory::this_symbol(), Variable::VAR,
false, Variable::THIS);
var->rewrite_ = new Slot(var, Slot::PARAMETER, -1); var->rewrite_ = new Slot(var, Slot::PARAMETER, -1);
receiver_ = new VariableProxy(Factory::this_symbol(), true, false); receiver_ = new VariableProxy(Factory::this_symbol(), true, false);
receiver_->BindTo(var); receiver_->BindTo(var);
@ -179,7 +180,8 @@ void Scope::Initialize(bool inside_with) {
// Declare 'arguments' variable which exists in all functions. // Declare 'arguments' variable which exists in all functions.
// Note that it may never be accessed, in which case it won't // Note that it may never be accessed, in which case it won't
// be allocated during variable allocation. // be allocated during variable allocation.
Declare(Factory::arguments_symbol(), Variable::VAR); locals_.Declare(this, Factory::arguments_symbol(), Variable::VAR,
true, Variable::ARGUMENTS);
} }
} }
@ -203,7 +205,7 @@ Variable* Scope::Lookup(Handle<String> name) {
Variable* Scope::DeclareFunctionVar(Handle<String> name) { Variable* Scope::DeclareFunctionVar(Handle<String> name) {
ASSERT(is_function_scope() && function_ == NULL); ASSERT(is_function_scope() && function_ == NULL);
function_ = new Variable(this, name, Variable::CONST, true, false); function_ = new Variable(this, name, Variable::CONST, true, Variable::NORMAL);
return function_; return function_;
} }
@ -213,7 +215,7 @@ Variable* Scope::Declare(Handle<String> name, Variable::Mode mode) {
// INTERNAL variables are allocated explicitly, and TEMPORARY // INTERNAL variables are allocated explicitly, and TEMPORARY
// variables are allocated via NewTemporary(). // variables are allocated via NewTemporary().
ASSERT(mode == Variable::VAR || mode == Variable::CONST); ASSERT(mode == Variable::VAR || mode == Variable::CONST);
return locals_.Declare(this, name, mode, true, false); return locals_.Declare(this, name, mode, true, Variable::NORMAL);
} }
@ -247,7 +249,8 @@ void Scope::RemoveUnresolved(VariableProxy* var) {
VariableProxy* Scope::NewTemporary(Handle<String> name) { VariableProxy* Scope::NewTemporary(Handle<String> name) {
Variable* var = new Variable(this, name, Variable::TEMPORARY, true, false); Variable* var = new Variable(this, name, Variable::TEMPORARY, true,
Variable::NORMAL);
VariableProxy* tmp = new VariableProxy(name, false, false); VariableProxy* tmp = new VariableProxy(name, false, false);
tmp->BindTo(var); tmp->BindTo(var);
temps_.Add(var); temps_.Add(var);
@ -503,7 +506,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
Variable* var = map->Lookup(name); Variable* var = map->Lookup(name);
if (var == NULL) { if (var == NULL) {
// Declare a new non-local. // Declare a new non-local.
var = map->Declare(NULL, name, mode, true, false); var = map->Declare(NULL, name, mode, true, Variable::NORMAL);
// Allocate it by giving it a dynamic lookup. // Allocate it by giving it a dynamic lookup.
var->rewrite_ = new Slot(var, Slot::LOOKUP, -1); var->rewrite_ = new Slot(var, Slot::LOOKUP, -1);
} }
@ -619,7 +622,7 @@ void Scope::ResolveVariable(Scope* global_scope,
// We must have a global variable. // We must have a global variable.
ASSERT(global_scope != NULL); ASSERT(global_scope != NULL);
var = new Variable(global_scope, proxy->name(), var = new Variable(global_scope, proxy->name(),
Variable::DYNAMIC, true, false); Variable::DYNAMIC, true, Variable::NORMAL);
} else if (scope_inside_with_) { } else if (scope_inside_with_) {
// If we are inside a with statement we give up and look up // If we are inside a with statement we give up and look up
@ -797,7 +800,7 @@ void Scope::AllocateParameterLocals() {
// are never allocated in the context). // are never allocated in the context).
Variable* arguments_shadow = Variable* arguments_shadow =
new Variable(this, Factory::arguments_shadow_symbol(), new Variable(this, Factory::arguments_shadow_symbol(),
Variable::INTERNAL, true, false); Variable::INTERNAL, true, Variable::ARGUMENTS);
arguments_shadow_ = arguments_shadow_ =
new VariableProxy(Factory::arguments_shadow_symbol(), false, false); new VariableProxy(Factory::arguments_shadow_symbol(), false, false);
arguments_shadow_->BindTo(arguments_shadow); arguments_shadow_->BindTo(arguments_shadow);

View File

@ -47,7 +47,7 @@ class LocalsMap: public HashMap {
virtual ~LocalsMap(); virtual ~LocalsMap();
Variable* Declare(Scope* scope, Handle<String> name, Variable::Mode mode, Variable* Declare(Scope* scope, Handle<String> name, Variable::Mode mode,
bool is_valid_LHS, bool is_this); bool is_valid_LHS, Variable::Kind kind);
Variable* Lookup(Handle<String> name); Variable* Lookup(Handle<String> name);
}; };

View File

@ -140,12 +140,12 @@ Variable::Variable(Scope* scope,
Handle<String> name, Handle<String> name,
Mode mode, Mode mode,
bool is_valid_LHS, bool is_valid_LHS,
bool is_this) Kind kind)
: scope_(scope), : scope_(scope),
name_(name), name_(name),
mode_(mode), mode_(mode),
is_valid_LHS_(is_valid_LHS), is_valid_LHS_(is_valid_LHS),
is_this_(is_this), kind_(kind),
local_if_not_shadowed_(NULL), local_if_not_shadowed_(NULL),
is_accessed_from_inner_scope_(false), is_accessed_from_inner_scope_(false),
rewrite_(NULL) { rewrite_(NULL) {

View File

@ -137,6 +137,12 @@ class Variable: public ZoneObject {
// in a context // in a context
}; };
enum Kind {
NORMAL,
THIS,
ARGUMENTS
};
// Printing support // Printing support
static const char* Mode2String(Mode mode); static const char* Mode2String(Mode mode);
@ -172,7 +178,8 @@ class Variable: public ZoneObject {
} }
bool is_global() const; bool is_global() const;
bool is_this() const { return is_this_; } bool is_this() const { return kind_ == THIS; }
bool is_arguments() const { return kind_ == ARGUMENTS; }
Variable* local_if_not_shadowed() const { Variable* local_if_not_shadowed() const {
ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL); ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
@ -190,13 +197,13 @@ class Variable: public ZoneObject {
private: private:
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS, Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
bool is_this); Kind kind);
Scope* scope_; Scope* scope_;
Handle<String> name_; Handle<String> name_;
Mode mode_; Mode mode_;
bool is_valid_LHS_; bool is_valid_LHS_;
bool is_this_; Kind kind_;
Variable* local_if_not_shadowed_; Variable* local_if_not_shadowed_;

View File

@ -1257,6 +1257,10 @@ void CodeGenerator::VisitCall(Call* node) {
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)' // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// TODO(X64): Consider optimizing Function.prototype.apply calls
// with arguments object. Requires lazy arguments allocation;
// see http://codereview.chromium.org/147075.
// Push the name of the function and the receiver onto the stack. // Push the name of the function and the receiver onto the stack.
frame_->Push(literal->handle()); frame_->Push(literal->handle());
Load(property->obj()); Load(property->obj());

View File

@ -0,0 +1,127 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function ReturnArguments() {
return arguments;
}
function ReturnReceiver() {
return this;
}
function Global() {
return ReturnArguments.apply(this, arguments);
}
assertEquals(0, Global().length);
assertEquals(1, Global(1).length);
assertEquals(2, Global(2)[0]);
assertEquals(2, Global(3, 4).length);
assertEquals(3, Global(3, 4)[0]);
assertEquals(4, Global(3, 4)[1]);
function Local() {
var object = { f: ReturnArguments };
return object.f.apply(this, arguments);
}
assertEquals(0, Local().length);
assertEquals(1, Local(1).length);
assertEquals(2, Local(2)[0]);
assertEquals(2, Local(3, 4).length);
assertEquals(3, Local(3, 4)[0]);
assertEquals(4, Local(3, 4)[1]);
function ShadowArguments() {
var arguments = [3, 4];
return ReturnArguments.apply(this, arguments);
}
assertEquals(2, ShadowArguments().length);
assertEquals(3, ShadowArguments()[0]);
assertEquals(4, ShadowArguments()[1]);
function NonObjectReceiver(receiver) {
return ReturnReceiver.apply(receiver, arguments);
}
assertEquals(42, NonObjectReceiver(42));
assertEquals("object", typeof NonObjectReceiver(42));
assertTrue(NonObjectReceiver(42) instanceof Number);
assertTrue(this === NonObjectReceiver(null));
assertTrue(this === NonObjectReceiver(void 0));
function ShadowApply() {
function f() { return 42; }
f.apply = function() { return 87; }
return f.apply(this, arguments);
}
assertEquals(87, ShadowApply());
assertEquals(87, ShadowApply(1));
assertEquals(87, ShadowApply(1, 2));
function CallNonFunction() {
var object = { apply: Function.prototype.apply };
return object.apply(this, arguments);
}
assertThrows(CallNonFunction, TypeError);
// Make sure that the stack after the apply optimization is
// in a valid state.
function SimpleStackCheck() {
var sentinel = 42;
var result = ReturnArguments.apply(this, arguments);
assertTrue(result != null);
assertEquals(42, sentinel);
}
SimpleStackCheck();
function ShadowArgumentsWithConstant() {
var arguments = null;
return ReturnArguments.apply(this, arguments);
}
assertEquals(0, ShadowArgumentsWithConstant().length);
assertEquals(0, ShadowArgumentsWithConstant(1).length);
assertEquals(0, ShadowArgumentsWithConstant(1, 2).length);
// Make sure we can deal with unfolding lots of arguments on the
// stack even in the presence of the apply optimizations.
var array = new Array(2048);
assertEquals(2048, Global.apply(this, array).length);

View File

@ -0,0 +1,47 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Make sure we don't allocate the arguments object over and
// over again.
function SharedLazyArguments() {
return arguments === arguments;
}
assertTrue(SharedLazyArguments());
// Make sure that accessing arguments doesn't clobber any
// local variables called arguments.
function ArgumentsOverride(x) {
var arguments = 42;
x = x ? x : 0;
return x + arguments;
}
assertEquals(42, ArgumentsOverride());
assertEquals(43, ArgumentsOverride(1));
assertEquals(44, ArgumentsOverride(2,3));