Index: src/ia32/codegen-ia32.cc |
=================================================================== |
--- src/ia32/codegen-ia32.cc (revision 2255) |
+++ src/ia32/codegen-ia32.cc (working copy) |
@@ -175,18 +175,7 @@ |
function_return_.set_direction(JumpTarget::BIDIRECTIONAL); |
function_return_is_shadowed_ = false; |
- // Allocate the arguments object and copy the parameters into it. |
- 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); |
- } |
- |
+ // Allocate the local context if needed. |
if (scope_->num_heap_slots() > 0) { |
Comment cmnt(masm_, "[ allocate local context"); |
// Allocate local context. |
@@ -247,27 +236,11 @@ |
} |
} |
- // 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 |
- // initialization because the arguments object may be stored in the |
- // context. |
- if (scope_->arguments() != NULL) { |
- Comment cmnt(masm_, "[ store arguments object"); |
- { 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. |
+ // initialization because the arguments object may be stored in |
+ // the context. |
+ if (ArgumentsMode() != NO_ARGUMENTS_ALLOCATION) { |
+ StoreArgumentsObject(true); |
} |
// Generate code to 'execute' declarations and initialize functions |
@@ -591,6 +564,68 @@ |
} |
+ArgumentsAllocationMode CodeGenerator::ArgumentsMode() const { |
+ if (scope_->arguments() == NULL) return NO_ARGUMENTS_ALLOCATION; |
+ ASSERT(scope_->arguments_shadow() != NULL); |
+ return (scope_->num_heap_slots() > 0) |
Mads Ager (chromium)
2009/06/24 07:52:17
Could you add a short comment explaining the reaso
|
+ ? 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) |
: cgen_(cgen), expression_(expression), type_(ILLEGAL) { |
cgen->LoadReference(this); |
@@ -2090,6 +2125,163 @@ |
} |
+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_->Pop(); |
+ |
+ // 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. |
+ __ mov(edx, Operand(esp, 0 * kPointerSize)); |
+ __ test(edx, Immediate(kSmiTagMask)); |
+ build_args.Branch(zero); |
+ __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ecx); |
+ build_args.Branch(less); |
+ __ cmp(ecx, LAST_JS_OBJECT_TYPE); |
+ build_args.Branch(greater); |
+ |
+ // Verify that we're invoking is Function.prototype.apply. |
Mads Ager (chromium)
2009/06/24 07:52:17
invoking is -> invoking
|
+ __ mov(edx, Operand(esp, 1 * kPointerSize)); |
+ __ test(edx, Immediate(kSmiTagMask)); |
+ build_args.Branch(zero); |
+ __ CmpObjectType(edx, JS_FUNCTION_TYPE, ecx); |
+ build_args.Branch(not_equal); |
+ __ mov(edx, FieldOperand(edx, JSFunction::kSharedFunctionInfoOffset)); |
+ Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply)); |
+ __ cmp(FieldOperand(edx, 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 we to call on the stack, |
Mads Ager (chromium)
2009/06/24 07:52:17
we to -> to
|
+ // 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(); |
Kasper Lund
2009/06/24 06:55:07
It would be nice to find a better way of achieving
|
+ 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 { |
public: |
DeferredStackCheck() { |
@@ -3615,6 +3807,44 @@ |
} |
+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( |
Slot* slot, |
TypeofState typeof_state, |
@@ -3787,7 +4017,7 @@ |
void CodeGenerator::VisitSlot(Slot* node) { |
Comment cmnt(masm_, "[ Slot"); |
- LoadFromSlot(node, typeof_state()); |
+ LoadFromSlotCheckForArguments(node, typeof_state()); |
} |
@@ -4349,24 +4579,41 @@ |
// 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. |
- frame_->Push(literal->handle()); |
- Load(property->obj()); |
+ Handle<String> name = Handle<String>::cast(literal->handle()); |
- // Load the arguments. |
- int arg_count = args->length(); |
- for (int i = 0; i < arg_count; i++) { |
- Load(args->at(i)); |
+ if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION && |
+ name->IsEqualTo(CStrVector("apply")) && |
+ args->length() == 2 && |
+ 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 { |
// ------------------------------------------- |
// JavaScript example: 'array[index](1, 2, 3)' |
@@ -5838,7 +6085,7 @@ |
Comment cmnt(masm, "[ Load from Slot"); |
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); |
ASSERT(slot != NULL); |
- cgen_->LoadFromSlot(slot, typeof_state); |
+ cgen_->LoadFromSlotCheckForArguments(slot, typeof_state); |
break; |
} |