text: // 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.
  
  #include "v8.h"
  
  #if defined(V8_TARGET_ARCH_ARM)
  
  #include "codegen-inl.h"
  #include "register-allocator-inl.h"
  #include "scopes.h"
  #include "virtual-frame-inl.h"
  
  namespace v8 {
  namespace internal {
  
  #define __ ACCESS_MASM(masm())
  
  void VirtualFrame::PopToR1R0() {
    // Shuffle things around so the top of stack is in r0 and r1.
    MergeTOSTo(R0_R1_TOS);
    // Pop the two registers off the stack so they are detached from the frame.
    LowerHeight(2);
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  void VirtualFrame::PopToR1() {
    // Shuffle things around so the top of stack is only in r1.
    MergeTOSTo(R1_TOS);
    // Pop the register off the stack so it is detached from the frame.
    LowerHeight(1);
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  void VirtualFrame::PopToR0() {
    // Shuffle things around so the top of stack only in r0.
    MergeTOSTo(R0_TOS);
    // Pop the register off the stack so it is detached from the frame.
    LowerHeight(1);
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  void VirtualFrame::MergeTo(const VirtualFrame* expected, Condition cond) {
    if (Equals(expected)) return;
    ASSERT(expected->IsCompatibleWith(this));
    MergeTOSTo(expected->top_of_stack_state_, cond);
    ASSERT(register_allocation_map_ == expected->register_allocation_map_);
  }
  
  
  void VirtualFrame::MergeTo(VirtualFrame* expected, Condition cond) {
    if (Equals(expected)) return;
    expected->tos_known_smi_map_ &= tos_known_smi_map_;
    MergeTOSTo(expected->top_of_stack_state_, cond);
    ASSERT(register_allocation_map_ == expected->register_allocation_map_);
  }
  
  
  void VirtualFrame::MergeTOSTo(
      VirtualFrame::TopOfStack expected_top_of_stack_state, Condition cond) {
  #define CASE_NUMBER(a, b) ((a) * TOS_STATES + (b))
    switch (CASE_NUMBER(top_of_stack_state_, expected_top_of_stack_state)) {
      case CASE_NUMBER(NO_TOS_REGISTERS, NO_TOS_REGISTERS):
        break;
      case CASE_NUMBER(NO_TOS_REGISTERS, R0_TOS):
        __ pop(r0, cond);
        break;
      case CASE_NUMBER(NO_TOS_REGISTERS, R1_TOS):
        __ pop(r1, cond);
        break;
      case CASE_NUMBER(NO_TOS_REGISTERS, R0_R1_TOS):
        __ pop(r0, cond);
        __ pop(r1, cond);
        break;
      case CASE_NUMBER(NO_TOS_REGISTERS, R1_R0_TOS):
        __ pop(r1, cond);
        __ pop(r0, cond);
        break;
      case CASE_NUMBER(R0_TOS, NO_TOS_REGISTERS):
        __ push(r0, cond);
        break;
      case CASE_NUMBER(R0_TOS, R0_TOS):
        break;
      case CASE_NUMBER(R0_TOS, R1_TOS):
        __ mov(r1, r0, LeaveCC, cond);
        break;
      case CASE_NUMBER(R0_TOS, R0_R1_TOS):
        __ pop(r1, cond);
        break;
      case CASE_NUMBER(R0_TOS, R1_R0_TOS):
        __ mov(r1, r0, LeaveCC, cond);
        __ pop(r0, cond);
        break;
      case CASE_NUMBER(R1_TOS, NO_TOS_REGISTERS):
        __ push(r1, cond);
        break;
      case CASE_NUMBER(R1_TOS, R0_TOS):
        __ mov(r0, r1, LeaveCC, cond);
        break;
      case CASE_NUMBER(R1_TOS, R1_TOS):
        break;
      case CASE_NUMBER(R1_TOS, R0_R1_TOS):
        __ mov(r0, r1, LeaveCC, cond);
        __ pop(r1, cond);
        break;
      case CASE_NUMBER(R1_TOS, R1_R0_TOS):
        __ pop(r0, cond);
        break;
      case CASE_NUMBER(R0_R1_TOS, NO_TOS_REGISTERS):
        __ Push(r1, r0, cond);
        break;
      case CASE_NUMBER(R0_R1_TOS, R0_TOS):
        __ push(r1, cond);
        break;
      case CASE_NUMBER(R0_R1_TOS, R1_TOS):
        __ push(r1, cond);
        __ mov(r1, r0, LeaveCC, cond);
        break;
      case CASE_NUMBER(R0_R1_TOS, R0_R1_TOS):
        break;
      case CASE_NUMBER(R0_R1_TOS, R1_R0_TOS):
        __ Swap(r0, r1, ip, cond);
        break;
      case CASE_NUMBER(R1_R0_TOS, NO_TOS_REGISTERS):
        __ Push(r0, r1, cond);
        break;
      case CASE_NUMBER(R1_R0_TOS, R0_TOS):
        __ push(r0, cond);
        __ mov(r0, r1, LeaveCC, cond);
        break;
      case CASE_NUMBER(R1_R0_TOS, R1_TOS):
        __ push(r0, cond);
        break;
      case CASE_NUMBER(R1_R0_TOS, R0_R1_TOS):
        __ Swap(r0, r1, ip, cond);
        break;
      case CASE_NUMBER(R1_R0_TOS, R1_R0_TOS):
        break;
      default:
        UNREACHABLE();
  #undef CASE_NUMBER
    }
    // A conditional merge will be followed by a conditional branch and the
    // fall-through code will have an unchanged virtual frame state.  If the
    // merge is unconditional ('al'ways) then it might be followed by a fall
    // through.  We need to update the virtual frame state to match the code we
    // are falling into.  The final case is an unconditional merge followed by an
    // unconditional branch, in which case it doesn't matter what we do to the
    // virtual frame state, because the virtual frame will be invalidated.
    if (cond == al) {
      top_of_stack_state_ = expected_top_of_stack_state;
    }
  }
  
  
  void VirtualFrame::Enter() {
    Comment cmnt(masm(), "[ Enter JS frame");
  
  #ifdef DEBUG
    // Verify that r1 contains a JS function.  The following code relies
    // on r2 being available for use.
    if (FLAG_debug_code) {
      Label map_check, done;
      __ tst(r1, Operand(kSmiTagMask));
      __ b(ne, &map_check);
      __ stop("VirtualFrame::Enter - r1 is not a function (smi check).");
      __ bind(&map_check);
      __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
      __ b(eq, &done);
      __ stop("VirtualFrame::Enter - r1 is not a function (map check).");
      __ bind(&done);
    }
  #endif  // DEBUG
  
    // We are about to push four values to the frame.
    Adjust(4);
    __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit());
    // Adjust FP to point to saved FP.
    __ add(fp, sp, Operand(2 * kPointerSize));
  }
  
  
  void VirtualFrame::Exit() {
    Comment cmnt(masm(), "[ Exit JS frame");
    // Record the location of the JS exit code for patching when setting
    // break point.
    __ RecordJSReturn();
  
    // Drop the execution stack down to the frame pointer and restore the caller
    // frame pointer and return address.
    __ mov(sp, fp);
    __ ldm(ia_w, sp, fp.bit() | lr.bit());
  }
  
  
  void VirtualFrame::AllocateStackSlots() {
    int count = local_count();
    if (count > 0) {
      Comment cmnt(masm(), "[ Allocate space for locals");
      Adjust(count);
      // Initialize stack slots with 'undefined' value.
      __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
      __ LoadRoot(r2, Heap::kStackLimitRootIndex);
      if (count < kLocalVarBound) {
        // For less locals the unrolled loop is more compact.
        for (int i = 0; i < count; i++) {
          __ push(ip);
        }
      } else {
        // For more locals a loop in generated code is more compact.
        Label alloc_locals_loop;
        __ mov(r1, Operand(count));
        __ bind(&alloc_locals_loop);
        __ push(ip);
        __ sub(r1, r1, Operand(1), SetCC);
        __ b(ne, &alloc_locals_loop);
      }
    } else {
      __ LoadRoot(r2, Heap::kStackLimitRootIndex);
    }
    // Check the stack for overflow or a break request.
    // Put the lr setup instruction in the delay slot.  The kInstrSize is added
    // to the implicit 8 byte offset that always applies to operations with pc
    // and gives a return address 12 bytes down.
    masm()->add(lr, pc, Operand(Assembler::kInstrSize));
    masm()->cmp(sp, Operand(r2));
    StackCheckStub stub;
    // Call the stub if lower.
    masm()->mov(pc,
                Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
                        RelocInfo::CODE_TARGET),
                LeaveCC,
                lo);
  }
  
  
  
  void VirtualFrame::PushReceiverSlotAddress() {
    UNIMPLEMENTED();
  }
  
  
  void VirtualFrame::PushTryHandler(HandlerType type) {
    // Grow the expression stack by handler size less one (the return
    // address in lr is already counted by a call instruction).
    Adjust(kHandlerSize - 1);
    __ PushTryHandler(IN_JAVASCRIPT, type);
  }
  
  
  void VirtualFrame::CallJSFunction(int arg_count) {
    // InvokeFunction requires function in r1.
    PopToR1();
    SpillAll();
  
    // +1 for receiver.
    Forget(arg_count + 1);
    ASSERT(cgen()->HasValidEntryRegisters());
    ParameterCount count(arg_count);
    __ InvokeFunction(r1, count, CALL_FUNCTION);
    // Restore the context.
    __ ldr(cp, Context());
  }
  
  
  void VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) {
    SpillAll();
    Forget(arg_count);
    ASSERT(cgen()->HasValidEntryRegisters());
    __ CallRuntime(f, arg_count);
  }
  
  
  void VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
    SpillAll();
    Forget(arg_count);
    ASSERT(cgen()->HasValidEntryRegisters());
    __ CallRuntime(id, arg_count);
  }
  
  
  #ifdef ENABLE_DEBUGGER_SUPPORT
  void VirtualFrame::DebugBreak() {
    ASSERT(cgen()->HasValidEntryRegisters());
    __ DebugBreak();
  }
  #endif
  
  
  void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
                                   InvokeJSFlags flags,
                                   int arg_count) {
    Forget(arg_count);
    __ InvokeBuiltin(id, flags);
  }
  
  
  void VirtualFrame::CallLoadIC(Handle<String> name, RelocInfo::Mode mode) {
    Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
    PopToR0();
    SpillAll();
    __ mov(r2, Operand(name));
    CallCodeObject(ic, mode, 0);
  }
  
  
  void VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
    Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
    PopToR0();
    if (is_contextual) {
      SpillAll();
      __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
    } else {
      EmitPop(r1);
      SpillAll();
    }
    __ mov(r2, Operand(name));
    CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
  }
  
  
  void VirtualFrame::CallKeyedLoadIC() {
    Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
    PopToR1R0();
    SpillAll();
    CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
  }
  
  
  void VirtualFrame::CallKeyedStoreIC() {
    Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
    PopToR1R0();
    SpillAll();
    EmitPop(r2);
    CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
  }
  
  
  void VirtualFrame::CallCodeObject(Handle<Code> code,
                                    RelocInfo::Mode rmode,
                                    int dropped_args) {
    switch (code->kind()) {
      case Code::CALL_IC:
      case Code::KEYED_CALL_IC:
      case Code::FUNCTION:
        break;
      case Code::KEYED_LOAD_IC:
      case Code::LOAD_IC:
      case Code::KEYED_STORE_IC:
      case Code::STORE_IC:
        ASSERT(dropped_args == 0);
        break;
      case Code::BUILTIN:
        ASSERT(*code == Builtins::builtin(Builtins::JSConstructCall));
        break;
      default:
        UNREACHABLE();
        break;
    }
    Forget(dropped_args);
    ASSERT(cgen()->HasValidEntryRegisters());
    __ Call(code, rmode);
  }
  
  
  //    NO_TOS_REGISTERS, R0_TOS, R1_TOS, R1_R0_TOS, R0_R1_TOS.
  const bool VirtualFrame::kR0InUse[TOS_STATES] =
      { false,            true,   false,  true,      true };
  const bool VirtualFrame::kR1InUse[TOS_STATES] =
      { false,            false,  true,   true,      true };
  const int VirtualFrame::kVirtualElements[TOS_STATES] =
      { 0,                1,      1,      2,         2 };
  const Register VirtualFrame::kTopRegister[TOS_STATES] =
      { r0,               r0,     r1,     r1,        r0 };
  const Register VirtualFrame::kBottomRegister[TOS_STATES] =
      { r0,               r0,     r1,     r0,        r1 };
  const Register VirtualFrame::kAllocatedRegisters[
      VirtualFrame::kNumberOfAllocatedRegisters] = { r2, r3, r4, r5, r6 };
  // Popping is done by the transition implied by kStateAfterPop.  Of course if
  // there were no stack slots allocated to registers then the physical SP must
  // be adjusted.
  const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPop[TOS_STATES] =
      { NO_TOS_REGISTERS, NO_TOS_REGISTERS, NO_TOS_REGISTERS, R0_TOS, R1_TOS };
  // Pushing is done by the transition implied by kStateAfterPush.  Of course if
  // the maximum number of registers was already allocated to the top of stack
  // slots then one register must be physically pushed onto the stack.
  const VirtualFrame::TopOfStack VirtualFrame::kStateAfterPush[TOS_STATES] =
      { R0_TOS, R1_R0_TOS, R0_R1_TOS, R0_R1_TOS, R1_R0_TOS };
  
  
  bool VirtualFrame::SpilledScope::is_spilled_ = false;
  
  
  void VirtualFrame::Drop(int count) {
    ASSERT(count >= 0);
    ASSERT(height() >= count);
    // Discard elements from the virtual frame and free any registers.
    int num_virtual_elements = kVirtualElements[top_of_stack_state_];
    while (num_virtual_elements > 0) {
      Pop();
      num_virtual_elements--;
      count--;
      if (count == 0) return;
    }
    if (count == 0) return;
    __ add(sp, sp, Operand(count * kPointerSize));
    LowerHeight(count);
  }
  
  
  void VirtualFrame::Pop() {
    if (top_of_stack_state_ == NO_TOS_REGISTERS) {
      __ add(sp, sp, Operand(kPointerSize));
    } else {
      top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
    }
    LowerHeight(1);
  }
  
  
  void VirtualFrame::EmitPop(Register reg) {
    ASSERT(!is_used(RegisterAllocator::ToNumber(reg)));
    if (top_of_stack_state_ == NO_TOS_REGISTERS) {
      __ pop(reg);
    } else {
      __ mov(reg, kTopRegister[top_of_stack_state_]);
      top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
    }
    LowerHeight(1);
  }
  
  
  void VirtualFrame::SpillAllButCopyTOSToR0() {
    switch (top_of_stack_state_) {
      case NO_TOS_REGISTERS:
        __ ldr(r0, MemOperand(sp, 0));
        break;
      case R0_TOS:
        __ push(r0);
        break;
      case R1_TOS:
        __ push(r1);
        __ mov(r0, r1);
        break;
      case R0_R1_TOS:
        __ Push(r1, r0);
        break;
      case R1_R0_TOS:
        __ Push(r0, r1);
        __ mov(r0, r1);
        break;
      default:
        UNREACHABLE();
    }
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  void VirtualFrame::SpillAllButCopyTOSToR1() {
    switch (top_of_stack_state_) {
      case NO_TOS_REGISTERS:
        __ ldr(r1, MemOperand(sp, 0));
        break;
      case R0_TOS:
        __ push(r0);
        __ mov(r1, r0);
        break;
      case R1_TOS:
        __ push(r1);
        break;
      case R0_R1_TOS:
        __ Push(r1, r0);
        __ mov(r1, r0);
        break;
      case R1_R0_TOS:
        __ Push(r0, r1);
        break;
      default:
        UNREACHABLE();
    }
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  void VirtualFrame::SpillAllButCopyTOSToR1R0() {
    switch (top_of_stack_state_) {
      case NO_TOS_REGISTERS:
        __ ldr(r1, MemOperand(sp, 0));
        __ ldr(r0, MemOperand(sp, kPointerSize));
        break;
      case R0_TOS:
        __ push(r0);
        __ mov(r1, r0);
        __ ldr(r0, MemOperand(sp, kPointerSize));
        break;
      case R1_TOS:
        __ push(r1);
        __ ldr(r0, MemOperand(sp, kPointerSize));
        break;
      case R0_R1_TOS:
        __ Push(r1, r0);
        __ Swap(r0, r1, ip);
        break;
      case R1_R0_TOS:
        __ Push(r0, r1);
        break;
      default:
        UNREACHABLE();
    }
    top_of_stack_state_ = NO_TOS_REGISTERS;
  }
  
  
  Register VirtualFrame::Peek() {
    AssertIsNotSpilled();
    if (top_of_stack_state_ == NO_TOS_REGISTERS) {
      top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
      Register answer = kTopRegister[top_of_stack_state_];
      __ pop(answer);
      return answer;
    } else {
      return kTopRegister[top_of_stack_state_];
    }
  }
  
  
  Register VirtualFrame::Peek2() {
    AssertIsNotSpilled();
    switch (top_of_stack_state_) {
      case NO_TOS_REGISTERS:
      case R0_TOS:
      case R0_R1_TOS:
        MergeTOSTo(R0_R1_TOS);
        return r1;
      case R1_TOS:
      case R1_R0_TOS:
        MergeTOSTo(R1_R0_TOS);
        return r0;
    }
    UNREACHABLE();
    return no_reg;
  }
  
  
  void VirtualFrame::Dup() {
    if (SpilledScope::is_spilled()) {
      __ ldr(ip, MemOperand(sp, 0));
      __ push(ip);
    } else {
      switch (top_of_stack_state_) {
        case NO_TOS_REGISTERS:
          __ ldr(r0, MemOperand(sp, 0));
          top_of_stack_state_ = R0_TOS;
          break;
        case R0_TOS:
          __ mov(r1, r0);
          // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R1_TOS:
          __ mov(r0, r1);
          // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R0_R1_TOS:
          __ push(r1);
          __ mov(r1, r0);
          // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R1_R0_TOS:
          __ push(r0);
          __ mov(r0, r1);
          // r0 and r1 contains the same value. Prefer state with r0 holding TOS.
          top_of_stack_state_ = R0_R1_TOS;
          break;
        default:
          UNREACHABLE();
      }
    }
    RaiseHeight(1, tos_known_smi_map_ & 1);
  }
  
  
  void VirtualFrame::Dup2() {
    if (SpilledScope::is_spilled()) {
      __ ldr(ip, MemOperand(sp, kPointerSize));
      __ push(ip);
      __ ldr(ip, MemOperand(sp, kPointerSize));
      __ push(ip);
    } else {
      switch (top_of_stack_state_) {
        case NO_TOS_REGISTERS:
          __ ldr(r0, MemOperand(sp, 0));
          __ ldr(r1, MemOperand(sp, kPointerSize));
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R0_TOS:
          __ push(r0);
          __ ldr(r1, MemOperand(sp, kPointerSize));
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R1_TOS:
          __ push(r1);
          __ ldr(r0, MemOperand(sp, kPointerSize));
          top_of_stack_state_ = R1_R0_TOS;
          break;
        case R0_R1_TOS:
          __ Push(r1, r0);
          top_of_stack_state_ = R0_R1_TOS;
          break;
        case R1_R0_TOS:
          __ Push(r0, r1);
          top_of_stack_state_ = R1_R0_TOS;
          break;
        default:
          UNREACHABLE();
      }
    }
    RaiseHeight(2, tos_known_smi_map_ & 3);
  }
  
  
  Register VirtualFrame::PopToRegister(Register but_not_to_this_one) {
    ASSERT(but_not_to_this_one.is(r0) ||
           but_not_to_this_one.is(r1) ||
           but_not_to_this_one.is(no_reg));
    LowerHeight(1);
    if (top_of_stack_state_ == NO_TOS_REGISTERS) {
      if (but_not_to_this_one.is(r0)) {
        __ pop(r1);
        return r1;
      } else {
        __ pop(r0);
        return r0;
      }
    } else {
      Register answer = kTopRegister[top_of_stack_state_];
      ASSERT(!answer.is(but_not_to_this_one));
      top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
      return answer;
    }
  }
  
  
  void VirtualFrame::EnsureOneFreeTOSRegister() {
    if (kVirtualElements[top_of_stack_state_] == kMaxTOSRegisters) {
      __ push(kBottomRegister[top_of_stack_state_]);
      top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
      top_of_stack_state_ = kStateAfterPop[top_of_stack_state_];
    }
    ASSERT(kVirtualElements[top_of_stack_state_] != kMaxTOSRegisters);
  }
  
  
  void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
    RaiseHeight(1, info.IsSmi() ? 1 : 0);
    if (reg.is(cp)) {
      // If we are pushing cp then we are about to make a call and things have to
      // be pushed to the physical stack.  There's nothing to be gained my moving
      // to a TOS register and then pushing that, we might as well push to the
      // physical stack immediately.
      MergeTOSTo(NO_TOS_REGISTERS);
      __ push(reg);
      return;
    }
    if (SpilledScope::is_spilled()) {
      ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
      __ push(reg);
      return;
    }
    if (top_of_stack_state_ == NO_TOS_REGISTERS) {
      if (reg.is(r0)) {
        top_of_stack_state_ = R0_TOS;
        return;
      }
      if (reg.is(r1)) {
        top_of_stack_state_ = R1_TOS;
        return;
      }
    }
    EnsureOneFreeTOSRegister();
    top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
    Register dest = kTopRegister[top_of_stack_state_];
    __ Move(dest, reg);
  }
  
  
  void VirtualFrame::SetElementAt(Register reg, int this_far_down) {
    if (this_far_down < kTOSKnownSmiMapSize) {
      tos_known_smi_map_ &= ~(1 << this_far_down);
    }
    if (this_far_down == 0) {
      Pop();
      Register dest = GetTOSRegister();
      if (dest.is(reg)) {
        // We already popped one item off the top of the stack.  If the only
        // free register is the one we were asked to push then we have been
        // asked to push a register that was already in use, which cannot
        // happen.  It therefore folows that there are two free TOS registers:
        ASSERT(top_of_stack_state_ == NO_TOS_REGISTERS);
        dest = dest.is(r0) ? r1 : r0;
      }
      __ mov(dest, reg);
      EmitPush(dest);
    } else if (this_far_down == 1) {
      int virtual_elements = kVirtualElements[top_of_stack_state_];
      if (virtual_elements < 2) {
        __ str(reg, ElementAt(this_far_down));
      } else {
        ASSERT(virtual_elements == 2);
        ASSERT(!reg.is(r0));
        ASSERT(!reg.is(r1));
        Register dest = kBottomRegister[top_of_stack_state_];
        __ mov(dest, reg);
      }
    } else {
      ASSERT(this_far_down >= 2);
      ASSERT(kVirtualElements[top_of_stack_state_] <= 2);
      __ str(reg, ElementAt(this_far_down));
    }
  }
  
  
  Register VirtualFrame::GetTOSRegister() {
    if (SpilledScope::is_spilled()) return r0;
  
    EnsureOneFreeTOSRegister();
    return kTopRegister[kStateAfterPush[top_of_stack_state_]];
  }
  
  
  void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
    RaiseHeight(1, info.IsSmi() ? 1 : 0);
    if (SpilledScope::is_spilled()) {
      __ mov(r0, operand);
      __ push(r0);
      return;
    }
    EnsureOneFreeTOSRegister();
    top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
    __ mov(kTopRegister[top_of_stack_state_], operand);
  }
  
  
  void VirtualFrame::EmitPush(MemOperand operand, TypeInfo info) {
    RaiseHeight(1, info.IsSmi() ? 1 : 0);
    if (SpilledScope::is_spilled()) {
      __ ldr(r0, operand);
      __ push(r0);
      return;
    }
    EnsureOneFreeTOSRegister();
    top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
    __ ldr(kTopRegister[top_of_stack_state_], operand);
  }
  
  
  void VirtualFrame::EmitPushRoot(Heap::RootListIndex index) {
    RaiseHeight(1, 0);
    if (SpilledScope::is_spilled()) {
      __ LoadRoot(r0, index);
      __ push(r0);
      return;
    }
    EnsureOneFreeTOSRegister();
    top_of_stack_state_ = kStateAfterPush[top_of_stack_state_];
    __ LoadRoot(kTopRegister[top_of_stack_state_], index);
  }
  
  
  void VirtualFrame::EmitPushMultiple(int count, int src_regs) {
    ASSERT(SpilledScope::is_spilled());
    Adjust(count);
    __ stm(db_w, sp, src_regs);
  }
  
  
  void VirtualFrame::SpillAll() {
    switch (top_of_stack_state_) {
      case R1_R0_TOS:
        masm()->push(r0);
        // Fall through.
      case R1_TOS:
        masm()->push(r1);
        top_of_stack_state_ = NO_TOS_REGISTERS;
        break;
      case R0_R1_TOS:
        masm()->push(r1);
        // Fall through.
      case R0_TOS:
        masm()->push(r0);
        top_of_stack_state_ = NO_TOS_REGISTERS;
        // Fall through.
      case NO_TOS_REGISTERS:
        break;
      default:
        UNREACHABLE();
        break;
    }
    ASSERT(register_allocation_map_ == 0);  // Not yet implemented.
  }
  
  #undef __
  
  } }  // namespace v8::internal
  
  #endif  // V8_TARGET_ARCH_ARM
  