Chromium Code Reviews| 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; |
| } |