// 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(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 name, RelocInfo::Mode mode) { Handle ic(Builtins::builtin(Builtins::LoadIC_Initialize)); PopToR0(); SpillAll(); __ mov(r2, Operand(name)); CallCodeObject(ic, mode, 0); } void VirtualFrame::CallStoreIC(Handle name, bool is_contextual) { Handle 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 ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); PopToR1R0(); SpillAll(); CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); } void VirtualFrame::CallKeyedStoreIC() { Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); PopToR1R0(); SpillAll(); EmitPop(r2); CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); } void VirtualFrame::CallCodeObject(Handle 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