From b64f6b92879b8bfaa55656f8047d491a5de3efcc Mon Sep 17 00:00:00 2001 From: "whesse@chromium.org" Date: Fri, 26 Jun 2009 08:38:48 +0000 Subject: [PATCH] X64 implementation: Add arguments object to context when needed. Review URL: http://codereview.chromium.org/149063 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2279 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/x64/codegen-x64.cc | 141 +++++++++++++++++++++++++++++++++++++++++++++++-- src/x64/codegen-x64.h | 14 +++++ 2 files changed, 150 insertions(+), 5 deletions(-) diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index ebadcd7..a5326cf 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -249,10 +249,10 @@ void CodeGenerator::GenCode(FunctionLiteral* function) { // Entry: // Stack: receiver, arguments, return address. - // ebp: caller's frame pointer - // esp: stack pointer - // edi: called JS function - // esi: callee's context + // rbp: caller's frame pointer + // rsp: stack pointer + // rdi: called JS function + // rsi: callee's context allocator_->Initialize(); frame_->Enter(); @@ -263,7 +263,73 @@ void CodeGenerator::GenCode(FunctionLiteral* function) { function_return_.set_direction(JumpTarget::BIDIRECTIONAL); function_return_is_shadowed_ = false; - // TODO(X64): Add code to handle arguments object and context object. + // Allocate the local context if needed. + if (scope_->num_heap_slots() > 0) { + Comment cmnt(masm_, "[ allocate local context"); + // Allocate local context. + // Get outer context and create a new context based on it. + frame_->PushFunction(); + Result context = frame_->CallRuntime(Runtime::kNewContext, 1); + + // Update context local. + frame_->SaveContextRegister(); + + // Verify that the runtime call result and rsi agree. + if (FLAG_debug_code) { + __ cmpq(context.reg(), rsi); + __ Assert(equal, "Runtime::NewContext should end up in rsi"); + } + } + + // TODO(1241774): Improve this code: + // 1) only needed if we have a context + // 2) no need to recompute context ptr every single time + // 3) don't copy parameter operand code from SlotOperand! + { + Comment cmnt2(masm_, "[ copy context parameters into .context"); + + // Note that iteration order is relevant here! If we have the same + // parameter twice (e.g., function (x, y, x)), and that parameter + // needs to be copied into the context, it must be the last argument + // passed to the parameter that needs to be copied. This is a rare + // case so we don't check for it, instead we rely on the copying + // order: such a parameter is copied repeatedly into the same + // context location and thus the last value is what is seen inside + // the function. + for (int i = 0; i < scope_->num_parameters(); i++) { + Variable* par = scope_->parameter(i); + Slot* slot = par->slot(); + if (slot != NULL && slot->type() == Slot::CONTEXT) { + // The use of SlotOperand below is safe in unspilled code + // because the slot is guaranteed to be a context slot. + // + // There are no parameters in the global scope. + ASSERT(!scope_->is_global_scope()); + frame_->PushParameterAt(i); + Result value = frame_->Pop(); + value.ToRegister(); + + // SlotOperand loads context.reg() with the context object + // stored to, used below in RecordWrite. + Result context = allocator_->Allocate(); + ASSERT(context.is_valid()); + __ movq(SlotOperand(slot, context.reg()), value.reg()); + int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + Result scratch = allocator_->Allocate(); + ASSERT(scratch.is_valid()); + frame_->Spill(context.reg()); + frame_->Spill(value.reg()); + __ RecordWrite(context.reg(), offset, value.reg(), scratch.reg()); + } + } + } + + // Store the arguments object. This must happen after context + // 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 // (source elements). In case of an illegal redeclaration we need to @@ -3483,6 +3549,71 @@ void CodeGenerator::LoadGlobalReceiver() { } +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(arguments.reg(), 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(); +} + + // TODO(1241834): Get rid of this function in favor of just using Load, now // that we have the INSIDE_TYPEOF typeof state. => Need to handle global // variables w/o reference errors elsewhere. diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 19ad8a3..af82de8 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -273,6 +273,14 @@ class CodeGenState BASE_EMBEDDED { }; +// ------------------------------------------------------------------------- +// Arguments allocation mode + +enum ArgumentsAllocationMode { + NO_ARGUMENTS_ALLOCATION, + EAGER_ARGUMENTS_ALLOCATION, + LAZY_ARGUMENTS_ALLOCATION +}; // ------------------------------------------------------------------------- @@ -383,6 +391,12 @@ class CodeGenerator: public AstVisitor { // target (which can not be done more than once). 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. void LoadReference(Reference* ref); void UnloadReference(Reference* ref); -- 2.7.4