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