Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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/riscv64/MoveEmitter-riscv64.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
void MoveEmitterRiscv64::breakCycle(const MoveOperand& from,
const MoveOperand& to, MoveOp::Type type,
uint32_t slotId) {
// 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:
if (to.isMemory()) {
ScratchFloat32Scope fpscratch32(masm);
masm.loadFloat32(getAdjustedAddress(to), fpscratch32);
masm.storeFloat32(fpscratch32, cycleSlot(slotId));
} else {
masm.storeFloat32(to.floatReg(), cycleSlot(slotId));
}
break;
case MoveOp::DOUBLE:
if (to.isMemory()) {
ScratchDoubleScope fpscratch64(masm);
masm.loadDouble(getAdjustedAddress(to), fpscratch64);
masm.storeDouble(fpscratch64, cycleSlot(slotId));
} else {
masm.storeDouble(to.floatReg(), cycleSlot(slotId));
}
break;
case MoveOp::INT32:
if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.load32(getAdjustedAddress(to), scratch2);
masm.store32(scratch2, cycleSlot(0));
} else {
masm.store32(to.reg(), cycleSlot(0));
}
break;
case MoveOp::GENERAL:
if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.loadPtr(getAdjustedAddress(to), scratch2);
masm.storePtr(scratch2, cycleSlot(0));
} else {
masm.storePtr(to.reg(), cycleSlot(0));
}
break;
default:
MOZ_CRASH("Unexpected move type");
}
}
void MoveEmitterRiscv64::completeCycle(const MoveOperand& from,
const MoveOperand& to, MoveOp::Type type,
uint32_t slotId) {
// 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:
if (to.isMemory()) {
ScratchFloat32Scope fpscratch32(masm);
masm.loadFloat32(cycleSlot(slotId), fpscratch32);
masm.storeFloat32(fpscratch32, getAdjustedAddress(to));
} else {
masm.loadFloat32(cycleSlot(slotId), to.floatReg());
}
break;
case MoveOp::DOUBLE:
if (to.isMemory()) {
ScratchDoubleScope fpscratch64(masm);
masm.loadDouble(cycleSlot(slotId), fpscratch64);
masm.storeDouble(fpscratch64, getAdjustedAddress(to));
} else {
masm.loadDouble(cycleSlot(slotId), to.floatReg());
}
break;
case MoveOp::INT32:
MOZ_ASSERT(slotId == 0);
if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.load32(cycleSlot(0), scratch2);
masm.store32(scratch2, getAdjustedAddress(to));
} else {
masm.load32(cycleSlot(0), to.reg());
}
break;
case MoveOp::GENERAL:
MOZ_ASSERT(slotId == 0);
if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.loadPtr(cycleSlot(0), scratch2);
masm.storePtr(scratch2, getAdjustedAddress(to));
} else {
masm.loadPtr(cycleSlot(0), to.reg());
}
break;
default:
MOZ_CRASH("Unexpected move type");
}
}
void MoveEmitterRiscv64::emit(const MoveResolver& moves) {
if (moves.numCycles()) {
// Reserve stack for cycle resolution
static_assert(SpillSlotSize == 8);
masm.reserveStack(moves.numCycles() * SpillSlotSize);
pushedAtCycle_ = masm.framePushed();
}
for (size_t i = 0; i < moves.numMoves(); i++) {
emit(moves.getMove(i));
}
}
void MoveEmitterRiscv64::emit(const MoveOp& move) {
const MoveOperand& from = move.from();
const MoveOperand& to = move.to();
if (move.isCycleEnd() && move.isCycleBegin()) {
// A fun consequence of aliased registers is you can have multiple
// cycles at once, and one can end exactly where another begins.
breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
completeCycle(from, to, move.type(), move.cycleEndSlot());
return;
}
if (move.isCycleEnd()) {
MOZ_ASSERT(inCycle_);
completeCycle(from, to, move.type(), move.cycleEndSlot());
MOZ_ASSERT(inCycle_ > 0);
inCycle_--;
return;
}
if (move.isCycleBegin()) {
breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
inCycle_++;
}
switch (move.type()) {
case MoveOp::FLOAT32:
emitFloat32Move(from, to);
break;
case MoveOp::DOUBLE:
emitDoubleMove(from, to);
break;
case MoveOp::INT32:
emitInt32Move(from, to);
break;
case MoveOp::GENERAL:
emitMove(from, to);
break;
default:
MOZ_CRASH("Unexpected move type");
}
}
void MoveEmitterRiscv64::emitMove(const MoveOperand& from,
const MoveOperand& to) {
if (from.isGeneralReg()) {
if (to.isGeneralReg()) {
masm.movePtr(from.reg(), to.reg());
} else if (to.isMemory()) {
masm.storePtr(from.reg(), getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitMove arguments.");
}
} else if (from.isMemory()) {
if (to.isGeneralReg()) {
masm.loadPtr(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.loadPtr(getAdjustedAddress(from), scratch2);
masm.storePtr(scratch2, getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitMove arguments.");
}
} else if (from.isEffectiveAddress()) {
if (to.isGeneralReg()) {
masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.computeEffectiveAddress(getAdjustedAddress(from), scratch2);
masm.storePtr(scratch2, getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitMove arguments.");
}
} else {
MOZ_CRASH("Invalid emitMove arguments.");
}
}
void MoveEmitterRiscv64::emitInt32Move(const MoveOperand& from,
const MoveOperand& to) {
if (from.isGeneralReg()) {
if (to.isGeneralReg()) {
masm.move32(from.reg(), to.reg());
} else if (to.isMemory()) {
masm.store32(from.reg(), getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitInt32Move arguments.");
}
} else if (from.isMemory()) {
if (to.isGeneralReg()) {
masm.load32(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.load32(getAdjustedAddress(from), scratch2);
masm.store32(scratch2, getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitInt32Move arguments.");
}
} else if (from.isEffectiveAddress()) {
if (to.isGeneralReg()) {
masm.computeEffectiveAddress(getAdjustedAddress(from), to.reg());
} else if (to.isMemory()) {
UseScratchRegisterScope temps(&masm);
Register scratch2 = temps.Acquire();
masm.computeEffectiveAddress(getAdjustedAddress(from), scratch2);
masm.store32(scratch2, getAdjustedAddress(to));
} else {
MOZ_CRASH("Invalid emitInt32Move arguments.");
}
} else {
MOZ_CRASH("Invalid emitInt32Move arguments.");
}
}
void MoveEmitterRiscv64::emitFloat32Move(const MoveOperand& from,
const MoveOperand& to) {
if (from.isFloatReg()) {
if (to.isFloatReg()) {
masm.fmv_s(to.floatReg(), from.floatReg());
} else if (to.isGeneralReg()) {
// This should only be used when passing float parameter in a1,a2,a3
MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
masm.fmv_x_w(to.reg(), from.floatReg());
} else {
MOZ_ASSERT(to.isMemory());
masm.storeFloat32(from.floatReg(), getAdjustedAddress(to));
}
} else if (to.isFloatReg()) {
MOZ_ASSERT(from.isMemory());
masm.loadFloat32(getAdjustedAddress(from), to.floatReg());
} else if (to.isGeneralReg()) {
MOZ_ASSERT(from.isMemory());
// This should only be used when passing float parameter in a1,a2,a3
MOZ_ASSERT(to.reg() == a1 || to.reg() == a2 || to.reg() == a3);
masm.loadPtr(getAdjustedAddress(from), to.reg());
} else {
MOZ_ASSERT(from.isMemory());
MOZ_ASSERT(to.isMemory());
ScratchFloat32Scope fpscratch32(masm);
masm.loadFloat32(getAdjustedAddress(from), fpscratch32);
masm.storeFloat32(fpscratch32, getAdjustedAddress(to));
}
}
void MoveEmitterRiscv64::emitDoubleMove(const MoveOperand& from,
const MoveOperand& to) {
if (from.isFloatReg()) {
if (to.isFloatReg()) {
masm.fmv_d(to.floatReg(), from.floatReg());
} else if (to.isGeneralReg()) {
masm.fmv_x_d(to.reg(), from.floatReg());
} else {
MOZ_ASSERT(to.isMemory());
masm.storeDouble(from.floatReg(), getAdjustedAddress(to));
}
} else if (to.isFloatReg()) {
if (from.isMemory()) {
masm.loadDouble(getAdjustedAddress(from), to.floatReg());
} else {
masm.fmv_d_x(to.floatReg(), from.reg());
}
} else {
MOZ_ASSERT(from.isMemory());
MOZ_ASSERT(to.isMemory());
ScratchDoubleScope fpscratch64(masm);
masm.loadDouble(getAdjustedAddress(from), fpscratch64);
masm.storeDouble(fpscratch64, getAdjustedAddress(to));
}
}
Address MoveEmitterRiscv64::cycleSlot(uint32_t slot, uint32_t subslot) const {
int32_t offset = masm.framePushed() - pushedAtCycle_;
return Address(StackPointer, offset + slot * sizeof(double) + subslot);
}
int32_t MoveEmitterRiscv64::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 MoveEmitterRiscv64::getAdjustedAddress(const MoveOperand& operand) {
return Address(operand.base(), getAdjustedOffset(operand));
}
void MoveEmitterRiscv64::assertDone() { MOZ_ASSERT(inCycle_ == 0); }
void MoveEmitterRiscv64::finish() {
assertDone();
masm.freeStack(masm.framePushed() - pushedAtStart_);
}