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/mips/MoveEmitter-mips.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: MoveEmitterMIPS::MoveEmitterMIPS(MacroAssemblerMIPSCompat &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: MoveEmitterMIPS::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: MoveEmitterMIPS::~MoveEmitterMIPS() michael@0: { michael@0: assertDone(); michael@0: } michael@0: michael@0: Address michael@0: MoveEmitterMIPS::cycleSlot() const michael@0: { michael@0: int offset = masm.framePushed() - pushedAtCycle_; michael@0: MOZ_ASSERT(Imm16::isInSignedRange(offset)); michael@0: return Address(StackPointer, offset); michael@0: } michael@0: michael@0: int32_t michael@0: MoveEmitterMIPS::getAdjustedOffset(const MoveOperand &operand) michael@0: { michael@0: MOZ_ASSERT(operand.isMemoryOrEffectiveAddress()); michael@0: if (operand.base() != StackPointer) michael@0: return operand.disp(); michael@0: michael@0: // Adjust offset if stack pointer has been moved. michael@0: return operand.disp() + masm.framePushed() - pushedAtStart_; michael@0: } michael@0: michael@0: Address michael@0: MoveEmitterMIPS::getAdjustedAddress(const MoveOperand &operand) michael@0: { michael@0: return Address(operand.base(), getAdjustedOffset(operand)); michael@0: } michael@0: michael@0: michael@0: Register michael@0: MoveEmitterMIPS::tempReg() michael@0: { michael@0: spilledReg_ = SecondScratchReg; michael@0: return SecondScratchReg; michael@0: } michael@0: michael@0: void michael@0: MoveEmitterMIPS::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: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.loadFloat32(getAdjustedAddress(to), temp); michael@0: masm.storeFloat32(temp, cycleSlot()); michael@0: } else { michael@0: masm.storeFloat32(to.floatReg(), cycleSlot()); michael@0: } michael@0: break; michael@0: case MoveOp::DOUBLE: michael@0: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.loadDouble(getAdjustedAddress(to), temp); michael@0: masm.storeDouble(temp, cycleSlot()); michael@0: } else { michael@0: masm.storeDouble(to.floatReg(), cycleSlot()); michael@0: } michael@0: break; michael@0: case MoveOp::INT32: michael@0: MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); michael@0: case MoveOp::GENERAL: michael@0: if (to.isMemory()) { michael@0: Register temp = tempReg(); michael@0: masm.loadPtr(getAdjustedAddress(to), temp); michael@0: masm.storePtr(temp, cycleSlot()); michael@0: } else { michael@0: // Second scratch register should not be moved by MoveEmitter. michael@0: MOZ_ASSERT(to.reg() != spilledReg_); michael@0: masm.storePtr(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: MoveEmitterMIPS::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: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.loadFloat32(cycleSlot(), temp); michael@0: masm.storeFloat32(temp, getAdjustedAddress(to)); michael@0: } else { michael@0: masm.loadFloat32(cycleSlot(), to.floatReg()); michael@0: } michael@0: break; michael@0: case MoveOp::DOUBLE: michael@0: if (to.isMemory()) { michael@0: FloatRegister temp = ScratchFloatReg; michael@0: masm.loadDouble(cycleSlot(), temp); michael@0: masm.storeDouble(temp, getAdjustedAddress(to)); michael@0: } else { michael@0: masm.loadDouble(cycleSlot(), to.floatReg()); michael@0: } michael@0: break; michael@0: case MoveOp::INT32: michael@0: MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); michael@0: case MoveOp::GENERAL: michael@0: if (to.isMemory()) { michael@0: Register temp = tempReg(); michael@0: masm.loadPtr(cycleSlot(), temp); michael@0: masm.storePtr(temp, getAdjustedAddress(to)); michael@0: } else { michael@0: // Second scratch register should not be moved by MoveEmitter. michael@0: MOZ_ASSERT(to.reg() != spilledReg_); michael@0: masm.loadPtr(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: MoveEmitterMIPS::emitMove(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: if (from.isGeneralReg()) { michael@0: // Second scratch register should not be moved by MoveEmitter. michael@0: MOZ_ASSERT(from.reg() != spilledReg_); michael@0: michael@0: if (to.isGeneralReg()) michael@0: masm.movePtr(from.reg(), to.reg()); michael@0: else if (to.isMemory()) michael@0: masm.storePtr(from.reg(), getAdjustedAddress(to)); michael@0: else michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); michael@0: } else if (from.isMemory()) { michael@0: if (to.isGeneralReg()) { michael@0: masm.loadPtr(getAdjustedAddress(from), to.reg()); michael@0: } else if (to.isMemory()) { michael@0: masm.loadPtr(getAdjustedAddress(from), tempReg()); michael@0: masm.storePtr(tempReg(), getAdjustedAddress(to)); michael@0: } else { michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); michael@0: } michael@0: } else if (from.isEffectiveAddress()) { michael@0: if (to.isGeneralReg()) { michael@0: masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg()); michael@0: } else if (to.isMemory()) { michael@0: masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg()); michael@0: masm.storePtr(tempReg(), getAdjustedAddress(to)); michael@0: } else { michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); michael@0: } michael@0: } else { michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitMove arguments."); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterMIPS::emitFloat32Move(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: // Ensure that we can use ScratchFloatReg in memory move. michael@0: MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); michael@0: MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); michael@0: michael@0: if (from.isFloatReg()) { michael@0: if (to.isFloatReg()) { michael@0: masm.moveFloat32(from.floatReg(), to.floatReg()); michael@0: } else if (to.isGeneralReg()) { michael@0: // This should only be used when passing float parameter in a1,a2,a3 michael@0: MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); michael@0: masm.moveFromFloat32(from.floatReg(), to.reg()); michael@0: } else { michael@0: MOZ_ASSERT(to.isMemory()); michael@0: masm.storeFloat32(from.floatReg(), getAdjustedAddress(to)); michael@0: } michael@0: } else if (to.isFloatReg()) { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: masm.loadFloat32(getAdjustedAddress(from), to.floatReg()); michael@0: } else if (to.isGeneralReg()) { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: // This should only be used when passing float parameter in a1,a2,a3 michael@0: MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3); michael@0: masm.loadPtr(getAdjustedAddress(from), to.reg()); michael@0: } else { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: MOZ_ASSERT(to.isMemory()); michael@0: masm.loadFloat32(getAdjustedAddress(from), ScratchFloatReg); michael@0: masm.storeFloat32(ScratchFloatReg, getAdjustedAddress(to)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterMIPS::emitDoubleMove(const MoveOperand &from, const MoveOperand &to) michael@0: { michael@0: // Ensure that we can use ScratchFloatReg in memory move. michael@0: MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg); michael@0: MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg); michael@0: michael@0: if (from.isFloatReg()) { michael@0: if (to.isFloatReg()) { michael@0: masm.moveDouble(from.floatReg(), to.floatReg()); michael@0: } else if (to.isGeneralReg()) { michael@0: // Used for passing double parameter in a2,a3 register pair. michael@0: // Two moves are added for one double parameter by michael@0: // MacroAssemblerMIPSCompat::passABIArg michael@0: if(to.reg() == a2) michael@0: masm.moveFromDoubleLo(from.floatReg(), a2); michael@0: else if(to.reg() == a3) michael@0: masm.moveFromDoubleHi(from.floatReg(), a3); michael@0: else michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); michael@0: } else { michael@0: MOZ_ASSERT(to.isMemory()); michael@0: masm.storeDouble(from.floatReg(), getAdjustedAddress(to)); michael@0: } michael@0: } else if (to.isFloatReg()) { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: masm.loadDouble(getAdjustedAddress(from), to.floatReg()); michael@0: } else if (to.isGeneralReg()) { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: // Used for passing double parameter in a2,a3 register pair. michael@0: // Two moves are added for one double parameter by michael@0: // MacroAssemblerMIPSCompat::passABIArg michael@0: if(to.reg() == a2) michael@0: masm.loadPtr(getAdjustedAddress(from), a2); michael@0: else if(to.reg() == a3) michael@0: masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3); michael@0: else michael@0: MOZ_ASSUME_UNREACHABLE("Invalid emitDoubleMove arguments."); michael@0: } else { michael@0: MOZ_ASSERT(from.isMemory()); michael@0: MOZ_ASSERT(to.isMemory()); michael@0: masm.loadDouble(getAdjustedAddress(from), ScratchFloatReg); michael@0: masm.storeDouble(ScratchFloatReg, getAdjustedAddress(to)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MoveEmitterMIPS::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: MOZ_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: MOZ_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: MOZ_ASSERT(sizeof(uintptr_t) == sizeof(int32_t)); 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: MoveEmitterMIPS::assertDone() michael@0: { michael@0: MOZ_ASSERT(!inCycle_); michael@0: } michael@0: michael@0: void michael@0: MoveEmitterMIPS::finish() michael@0: { michael@0: assertDone(); michael@0: michael@0: masm.freeStack(masm.framePushed() - pushedAtStart_); michael@0: }