blob: 45f48767c4da43089f55e2dec7f014e2a8a83cfa [file] [log] [blame]
// 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.
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(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;
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