michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/arm/MoveEmitter-arm.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: MoveEmitterARM::MoveEmitterARM(MacroAssemblerARMCompat &masm) michael@0: : inCycle_(false), michael@0: masm(masm), michael@0: pushedAtCycle_(-1), michael@0: pushedAtSpill_(-1), michael@0: spilledReg_(InvalidReg), michael@0: spilledFloatReg_(InvalidFloatReg) michael@0: { michael@0: pushedAtStart_ = masm.framePushed(); michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::emit(const MoveResolver &moves) michael@0: { michael@0: if (moves.hasCycles()) { michael@0: // Reserve stack for cycle resolution michael@0: masm.reserveStack(sizeof(double)); michael@0: pushedAtCycle_ = masm.framePushed(); michael@0: } michael@0: michael@0: for (size_t i = 0; i < moves.numMoves(); i++) michael@0: emit(moves.getMove(i)); michael@0: } michael@0: michael@0: MoveEmitterARM::~MoveEmitterARM() michael@0: { michael@0: assertDone(); michael@0: } michael@0: michael@0: Operand michael@0: MoveEmitterARM::cycleSlot() const michael@0: { michael@0: int offset = masm.framePushed() - pushedAtCycle_; michael@0: JS_ASSERT(offset < 4096 && offset > -4096); michael@0: return Operand(StackPointer, offset); michael@0: } michael@0: michael@0: // THIS IS ALWAYS AN LDRAddr. It should not be wrapped in an operand, methinks michael@0: Operand michael@0: MoveEmitterARM::spillSlot() const michael@0: { michael@0: int offset = masm.framePushed() - pushedAtSpill_; michael@0: JS_ASSERT(offset < 4096 && offset > -4096); michael@0: return Operand(StackPointer, offset); michael@0: } michael@0: michael@0: Operand michael@0: MoveEmitterARM::toOperand(const MoveOperand &operand, bool isFloat) const michael@0: { michael@0: if (operand.isMemoryOrEffectiveAddress()) { michael@0: if (operand.base() != StackPointer) { michael@0: JS_ASSERT(operand.disp() < 1024 && operand.disp() > -1024); michael@0: return Operand(operand.base(), operand.disp()); michael@0: } michael@0: michael@0: JS_ASSERT(operand.disp() >= 0); michael@0: michael@0: // Otherwise, the stack offset may need to be adjusted. michael@0: return Operand(StackPointer, operand.disp() + (masm.framePushed() - pushedAtStart_)); michael@0: } michael@0: michael@0: if (operand.isGeneralReg()) michael@0: return Operand(operand.reg()); michael@0: michael@0: JS_ASSERT(operand.isFloatReg()); michael@0: return Operand(operand.floatReg()); michael@0: } michael@0: michael@0: Register michael@0: MoveEmitterARM::tempReg() michael@0: { michael@0: if (spilledReg_ != InvalidReg) michael@0: return spilledReg_; michael@0: michael@0: // For now, just pick r12/ip as the eviction point. This is totally michael@0: // random, and if it ends up being bad, we can use actual heuristics later. michael@0: // r12 is actually a bad choice. it is the scratch register, which is frequently michael@0: // used for address computations, such as those found when we attempt to access michael@0: // values more than 4096 off of the stack pointer. michael@0: // instead, use lr, the LinkRegister. michael@0: spilledReg_ = r14; michael@0: if (pushedAtSpill_ == -1) { michael@0: masm.Push(spilledReg_); michael@0: pushedAtSpill_ = masm.framePushed(); michael@0: } else { michael@0: masm.ma_str(spilledReg_, spillSlot()); michael@0: } michael@0: return spilledReg_; michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::breakCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) michael@0: { michael@0: // There is some pattern: michael@0: // (A -> B) michael@0: // (B -> A) michael@0: // michael@0: // This case handles (A -> B), which we reach first. We save B, then allow michael@0: // the original move to continue. michael@0: switch (type) { michael@0: case MoveOp::FLOAT32: michael@0: case MoveOp::DOUBLE: michael@0: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.ma_vldr(toOperand(to, true), temp); michael@0: masm.ma_vstr(temp, cycleSlot()); michael@0: } else { michael@0: masm.ma_vstr(to.floatReg(), cycleSlot()); michael@0: } michael@0: break; michael@0: case MoveOp::INT32: michael@0: case MoveOp::GENERAL: michael@0: // an non-vfp value michael@0: if (to.isMemory()) { michael@0: Register temp = tempReg(); michael@0: masm.ma_ldr(toOperand(to, false), temp); michael@0: masm.ma_str(temp, cycleSlot()); michael@0: } else { michael@0: if (to.reg() == spilledReg_) { michael@0: // If the destination was spilled, restore it first. michael@0: masm.ma_ldr(spillSlot(), spilledReg_); michael@0: spilledReg_ = InvalidReg; michael@0: } michael@0: masm.ma_str(to.reg(), cycleSlot()); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected move type"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::completeCycle(const MoveOperand &from, const MoveOperand &to, MoveOp::Type type) michael@0: { michael@0: // There is some pattern: michael@0: // (A -> B) michael@0: // (B -> A) michael@0: // michael@0: // This case handles (B -> A), which we reach last. We emit a move from the michael@0: // saved value of B, to A. michael@0: switch (type) { michael@0: case MoveOp::FLOAT32: michael@0: case MoveOp::DOUBLE: michael@0: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.ma_vldr(cycleSlot(), temp); michael@0: masm.ma_vstr(temp, toOperand(to, true)); michael@0: } else { michael@0: masm.ma_vldr(cycleSlot(), to.floatReg()); michael@0: } michael@0: break; michael@0: case MoveOp::INT32: michael@0: case MoveOp::GENERAL: michael@0: if (to.isMemory()) { michael@0: Register temp = tempReg(); michael@0: masm.ma_ldr(cycleSlot(), temp); michael@0: masm.ma_str(temp, toOperand(to, false)); michael@0: } else { michael@0: if (to.reg() == spilledReg_) { michael@0: // Make sure we don't re-clobber the spilled register later. michael@0: spilledReg_ = InvalidReg; michael@0: } michael@0: masm.ma_ldr(cycleSlot(), to.reg()); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected move type"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::emitMove(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: if (to.isGeneralReg() && to.reg() == spilledReg_) { michael@0: // If the destination is the spilled register, make sure we michael@0: // don't re-clobber its value. michael@0: spilledReg_ = InvalidReg; michael@0: } michael@0: michael@0: if (from.isGeneralReg()) { michael@0: if (from.reg() == spilledReg_) { michael@0: // If the source is a register that has been spilled, make sure michael@0: // to load the source back into that register. michael@0: masm.ma_ldr(spillSlot(), spilledReg_); michael@0: spilledReg_ = InvalidReg; michael@0: } michael@0: switch (toOperand(to, false).getTag()) { michael@0: case Operand::OP2: michael@0: // secretly must be a register michael@0: masm.ma_mov(from.reg(), to.reg()); michael@0: break; michael@0: case Operand::MEM: michael@0: masm.ma_str(from.reg(), toOperand(to, false)); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("strange move!"); michael@0: } michael@0: } else if (to.isGeneralReg()) { michael@0: JS_ASSERT(from.isMemoryOrEffectiveAddress()); michael@0: if (from.isMemory()) michael@0: masm.ma_ldr(toOperand(from, false), to.reg()); michael@0: else michael@0: masm.ma_add(from.base(), Imm32(from.disp()), to.reg()); michael@0: } else { michael@0: // Memory to memory gpr move. michael@0: Register reg = tempReg(); michael@0: michael@0: JS_ASSERT(from.isMemoryOrEffectiveAddress()); michael@0: if (from.isMemory()) michael@0: masm.ma_ldr(toOperand(from, false), reg); michael@0: else michael@0: masm.ma_add(from.base(), Imm32(from.disp()), reg); michael@0: JS_ASSERT(to.base() != reg); michael@0: masm.ma_str(reg, toOperand(to, false)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::emitFloat32Move(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: if (from.isFloatReg()) { michael@0: if (to.isFloatReg()) michael@0: masm.ma_vmov_f32(from.floatReg(), to.floatReg()); michael@0: else michael@0: masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), michael@0: toOperand(to, true)); michael@0: } else if (to.isFloatReg()) { michael@0: masm.ma_vldr(toOperand(from, true), michael@0: VFPRegister(to.floatReg()).singleOverlay()); michael@0: } else { michael@0: // Memory to memory move. michael@0: JS_ASSERT(from.isMemory()); michael@0: FloatRegister reg = ScratchFloatReg; michael@0: masm.ma_vldr(toOperand(from, true), michael@0: VFPRegister(reg).singleOverlay()); michael@0: masm.ma_vstr(VFPRegister(reg).singleOverlay(), michael@0: toOperand(to, true)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: if (from.isFloatReg()) { michael@0: if (to.isFloatReg()) michael@0: masm.ma_vmov(from.floatReg(), to.floatReg()); michael@0: else michael@0: masm.ma_vstr(from.floatReg(), toOperand(to, true)); michael@0: } else if (to.isFloatReg()) { michael@0: masm.ma_vldr(toOperand(from, true), to.floatReg()); michael@0: } else { michael@0: // Memory to memory move. michael@0: JS_ASSERT(from.isMemory()); michael@0: FloatRegister reg = ScratchFloatReg; michael@0: masm.ma_vldr(toOperand(from, true), reg); michael@0: masm.ma_vstr(reg, toOperand(to, true)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::emit(const MoveOp &move) michael@0: { michael@0: const MoveOperand &from = move.from(); michael@0: const MoveOperand &to = move.to(); michael@0: michael@0: if (move.isCycleEnd()) { michael@0: JS_ASSERT(inCycle_); michael@0: completeCycle(from, to, move.type()); michael@0: inCycle_ = false; michael@0: return; michael@0: } michael@0: michael@0: if (move.isCycleBegin()) { michael@0: JS_ASSERT(!inCycle_); michael@0: breakCycle(from, to, move.endCycleType()); michael@0: inCycle_ = true; michael@0: } michael@0: michael@0: switch (move.type()) { michael@0: case MoveOp::FLOAT32: michael@0: emitFloat32Move(from, to); michael@0: break; michael@0: case MoveOp::DOUBLE: michael@0: emitDoubleMove(from, to); michael@0: break; michael@0: case MoveOp::INT32: michael@0: case MoveOp::GENERAL: michael@0: emitMove(from, to); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected move type"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::assertDone() michael@0: { michael@0: JS_ASSERT(!inCycle_); michael@0: } michael@0: michael@0: void michael@0: MoveEmitterARM::finish() michael@0: { michael@0: assertDone(); michael@0: michael@0: if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg) michael@0: masm.ma_ldr(spillSlot(), spilledReg_); michael@0: masm.freeStack(masm.framePushed() - pushedAtStart_); michael@0: }