blob: 00cfd223692e5e19f00f0bab336275758af2ef49 [file] [log] [blame]
/* -*- 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/mips/MoveEmitter-mips.h"
using namespace js;
using namespace js::jit;
MoveEmitterMIPS::MoveEmitterMIPS(MacroAssemblerMIPSCompat &masm)
: inCycle_(false),
masm(masm),
pushedAtCycle_(-1),
pushedAtSpill_(-1),
spilledReg_(InvalidReg),
spilledFloatReg_(InvalidFloatReg)
{
pushedAtStart_ = masm.framePushed();
}
void
MoveEmitterMIPS::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));
}
MoveEmitterMIPS::~MoveEmitterMIPS()
{
assertDone();
}
Address
MoveEmitterMIPS::cycleSlot() const
{
int offset = masm.framePushed() - pushedAtCycle_;
MOZ_ASSERT(Imm16::isInSignedRange(offset));
return Address(StackPointer, offset);
}
int32_t
MoveEmitterMIPS::getAdjustedOffset(const MoveOperand &operand)
{
MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());
if (operand.base() != StackPointer)
return operand.disp();
// Adjust offset if stack pointer has been moved.
return operand.disp() + masm.framePushed() - pushedAtStart_;
}
Address
MoveEmitterMIPS::getAdjustedAddress(const MoveOperand &operand)
{
return Address(operand.base(), getAdjustedOffset(operand));
}
Register
MoveEmitterMIPS::tempReg()
{
spilledReg_ = SecondScratchReg;
return SecondScratchReg;
}
void
MoveEmitterMIPS::breakCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind)
{
// 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 (kind) {
case Move::DOUBLE:
if (to.isMemory()) {
FloatRegister temp = ScratchFloatReg;
masm.loadDouble(getAdjustedAddress(to), temp);
masm.storeDouble(temp, cycleSlot());
} else {
masm.storeDouble(to.floatReg(), cycleSlot());
}
break;
case Move::GENERAL:
if (to.isMemory()) {
Register temp = tempReg();
masm.loadPtr(getAdjustedAddress(to), temp);
masm.storePtr(temp, cycleSlot());
} else {
// Second scratch register should not be moved by MoveEmitter.
MOZ_ASSERT(to.reg() != spilledReg_);
masm.storePtr(to.reg(), cycleSlot());
}
break;
default:
JS_NOT_REACHED("Unexpected move type");
}
}
void
MoveEmitterMIPS::completeCycle(const MoveOperand &from, const MoveOperand &to, Move::Kind kind)
{
// 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 (kind) {
case Move::DOUBLE:
if (to.isMemory()) {
FloatRegister temp = ScratchFloatReg;
masm.loadDouble(cycleSlot(), temp);
masm.storeDouble(temp, getAdjustedAddress(to));
} else {
masm.loadDouble(cycleSlot(), to.floatReg());
}
break;
case Move::GENERAL:
if (to.isMemory()) {
Register temp = tempReg();
masm.loadPtr(cycleSlot(), temp);
masm.storePtr(temp, getAdjustedAddress(to));
} else {
// Second scratch register should not be moved by MoveEmitter.
MOZ_ASSERT(to.reg() != spilledReg_);
masm.loadPtr(cycleSlot(), to.reg());
}
break;
default:
JS_NOT_REACHED("Unexpected move type");
}
}
void
MoveEmitterMIPS::emitMove(const MoveOperand &from, const MoveOperand &to)
{
if (from.isGeneralReg()) {
// Second scratch register should not be moved by MoveEmitter.
MOZ_ASSERT(from.reg() != spilledReg_);
if (to.isGeneralReg())
masm.movePtr(from.reg(), to.reg());
else if (to.isMemory())
masm.storePtr(from.reg(), getAdjustedAddress(to));
else
JS_NOT_REACHED("Invalid emitMove arguments.");
} else if (from.isMemory()) {
if (to.isGeneralReg()) {
masm.loadPtr(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
masm.loadPtr(getAdjustedAddress(from), tempReg());
masm.storePtr(tempReg(), getAdjustedAddress(to));
} else {
JS_NOT_REACHED("Invalid emitMove arguments.");
}
} else if (from.isEffectiveAddress()) {
if (to.isGeneralReg()) {
masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
masm.computeEffectiveAddress(getAdjustedAddress(from), tempReg());
masm.storePtr(tempReg(), getAdjustedAddress(to));
} else {
JS_NOT_REACHED("Invalid emitMove arguments.");
}
} else {
JS_NOT_REACHED("Invalid emitMove arguments.");
}
}
void
MoveEmitterMIPS::emitDoubleMove(const MoveOperand &from, const MoveOperand &to)
{
// Ensure that we can use ScratchFloatReg in memory move.
MOZ_ASSERT_IF(from.isFloatReg(), from.floatReg() != ScratchFloatReg);
MOZ_ASSERT_IF(to.isFloatReg(), to.floatReg() != ScratchFloatReg);
if (from.isFloatReg()) {
if (to.isFloatReg()) {
masm.moveDouble(from.floatReg(), to.floatReg());
} else if (to.isGeneralReg()) {
// Used for passing double parameter in a2,a3 register pair.
// Two moves are added for one double parameter by
// MacroAssemblerMIPSCompat::passABIArg
if(to.reg() == a2)
masm.moveFromDoubleLo(from.floatReg(), a2);
else if(to.reg() == a3)
masm.moveFromDoubleHi(from.floatReg(), a3);
else
JS_NOT_REACHED("Invalid emitDoubleMove arguments.");
} else {
MOZ_ASSERT(to.isMemory());
masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
}
} else if (to.isFloatReg()) {
MOZ_ASSERT(from.isMemory());
masm.loadDouble(getAdjustedAddress(from), to.floatReg());
} else if (to.isGeneralReg()) {
MOZ_ASSERT(from.isMemory());
// Used for passing double parameter in a2,a3 register pair.
// Two moves are added for one double parameter by
// MacroAssemblerMIPSCompat::passABIArg
if(to.reg() == a2)
masm.loadPtr(getAdjustedAddress(from), a2);
else if(to.reg() == a3)
masm.loadPtr(Address(from.base(), getAdjustedOffset(from) + sizeof(uint32_t)), a3);
else
JS_NOT_REACHED("Invalid emitDoubleMove arguments.");
} else {
MOZ_ASSERT(from.isMemory());
MOZ_ASSERT(to.isMemory());
masm.loadDouble(getAdjustedAddress(from), ScratchFloatReg);
masm.storeDouble(ScratchFloatReg, getAdjustedAddress(to));
}
}
void
MoveEmitterMIPS::emit(const Move &move)
{
const MoveOperand& from = move.from();
const MoveOperand& to = move.to();
if (move.inCycle()) {
if (inCycle_) {
completeCycle(from, to, move.kind());
inCycle_ = false;
return;
}
breakCycle(from, to, move.kind());
inCycle_ = true;
}
if (move.kind() == Move::DOUBLE) {
emitDoubleMove(from, to);
} else {
emitMove(from, to);
}
}
void
MoveEmitterMIPS::assertDone()
{
MOZ_ASSERT(!inCycle_);
}
void
MoveEmitterMIPS::finish()
{
assertDone();
masm.freeStack(masm.framePushed() - pushedAtStart_);
}