summaryrefslogtreecommitdiff
path: root/src/3rdparty/v8/src/arm/virtual-frame-arm.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/v8/src/arm/virtual-frame-arm.cc')
-rw-r--r--src/3rdparty/v8/src/arm/virtual-frame-arm.cc843
1 files changed, 843 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/arm/virtual-frame-arm.cc b/src/3rdparty/v8/src/arm/virtual-frame-arm.cc
new file mode 100644
index 0000000..a852d6e
--- /dev/null
+++ b/src/3rdparty/v8/src/arm/virtual-frame-arm.cc
@@ -0,0 +1,843 @@
+// 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->tos_known_smi_map_ & tos_known_smi_map_) ==
+ expected->tos_known_smi_map_);
+ 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;
+ tos_known_smi_map_ &= expected->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.
+ masm()->cmp(sp, Operand(r2));
+ StackCheckStub stub;
+ // Call the stub if lower.
+ masm()->mov(ip,
+ Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()),
+ RelocInfo::CODE_TARGET),
+ LeaveCC,
+ lo);
+ masm()->Call(ip, 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(const 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(Isolate::Current()->builtins()->builtin(
+ Builtins::kLoadIC_Initialize));
+ PopToR0();
+ SpillAll();
+ __ mov(r2, Operand(name));
+ CallCodeObject(ic, mode, 0);
+}
+
+
+void VirtualFrame::CallStoreIC(Handle<String> name,
+ bool is_contextual,
+ StrictModeFlag strict_mode) {
+ Handle<Code> ic(Isolate::Current()->builtins()->builtin(
+ (strict_mode == kStrictMode) ? Builtins::kStoreIC_Initialize_Strict
+ : Builtins::kStoreIC_Initialize));
+ PopToR0();
+ RelocInfo::Mode mode;
+ if (is_contextual) {
+ SpillAll();
+ __ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ mode = RelocInfo::CODE_TARGET_CONTEXT;
+ } else {
+ EmitPop(r1);
+ SpillAll();
+ mode = RelocInfo::CODE_TARGET;
+ }
+ __ mov(r2, Operand(name));
+ CallCodeObject(ic, mode, 0);
+}
+
+
+void VirtualFrame::CallKeyedLoadIC() {
+ Handle<Code> ic(Isolate::Current()->builtins()->builtin(
+ Builtins::kKeyedLoadIC_Initialize));
+ PopToR1R0();
+ SpillAll();
+ CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
+}
+
+
+void VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) {
+ Handle<Code> ic(Isolate::Current()->builtins()->builtin(
+ (strict_mode == kStrictMode) ? Builtins::kKeyedStoreIC_Initialize_Strict
+ : Builtins::kKeyedStoreIC_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 == Isolate::Current()->builtins()->builtin(
+ Builtins::kJSConstructCall));
+ 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 };
+
+
+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;
+ default:
+ 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