/* -*- 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/arm64/MoveEmitter-arm64.h" #include "jit/MacroAssembler-inl.h" using namespace js; using namespace js::jit; MemOperand MoveEmitterARM64::toMemOperand(const MoveOperand& operand) const { MOZ_ASSERT(operand.isMemory()); ARMRegister base(operand.base(), 64); if (operand.base() == masm.getStackPointer()) return MemOperand(base, operand.disp() + (masm.framePushed() - pushedAtStart_)); return MemOperand(base, operand.disp()); } void MoveEmitterARM64::emit(const MoveResolver& moves) { if (moves.numCycles()) { masm.reserveStack(sizeof(void*)); pushedAtCycle_ = masm.framePushed(); } for (size_t i = 0; i < moves.numMoves(); i++) emitMove(moves.getMove(i)); } void MoveEmitterARM64::finish() { assertDone(); masm.freeStack(masm.framePushed() - pushedAtStart_); MOZ_ASSERT(masm.framePushed() == pushedAtStart_); } void MoveEmitterARM64::emitMove(const MoveOp& move) { const MoveOperand& from = move.from(); const MoveOperand& to = move.to(); if (move.isCycleBegin()) { MOZ_ASSERT(!inCycle_ && !move.isCycleEnd()); breakCycle(from, to, move.endCycleType()); inCycle_ = true; } else if (move.isCycleEnd()) { MOZ_ASSERT(inCycle_); completeCycle(from, to, move.type()); inCycle_ = false; return; } 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: emitGeneralMove(from, to); break; default: MOZ_CRASH("Unexpected move type"); } } void MoveEmitterARM64::emitFloat32Move(const MoveOperand& from, const MoveOperand& to) { if (from.isFloatReg()) { if (to.isFloatReg()) masm.Fmov(toFPReg(to, MoveOp::FLOAT32), toFPReg(from, MoveOp::FLOAT32)); else masm.Str(toFPReg(from, MoveOp::FLOAT32), toMemOperand(to)); return; } if (to.isFloatReg()) { masm.Ldr(toFPReg(to, MoveOp::FLOAT32), toMemOperand(from)); return; } vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch32 = temps.AcquireS(); masm.Ldr(scratch32, toMemOperand(from)); masm.Str(scratch32, toMemOperand(to)); } void MoveEmitterARM64::emitDoubleMove(const MoveOperand& from, const MoveOperand& to) { if (from.isFloatReg()) { if (to.isFloatReg()) masm.Fmov(toFPReg(to, MoveOp::DOUBLE), toFPReg(from, MoveOp::DOUBLE)); else masm.Str(toFPReg(from, MoveOp::DOUBLE), toMemOperand(to)); return; } if (to.isFloatReg()) { masm.Ldr(toFPReg(to, MoveOp::DOUBLE), toMemOperand(from)); return; } vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch = temps.AcquireD(); masm.Ldr(scratch, toMemOperand(from)); masm.Str(scratch, toMemOperand(to)); } void MoveEmitterARM64::emitInt32Move(const MoveOperand& from, const MoveOperand& to) { if (from.isGeneralReg()) { if (to.isGeneralReg()) masm.Mov(toARMReg32(to), toARMReg32(from)); else masm.Str(toARMReg32(from), toMemOperand(to)); return; } if (to.isGeneralReg()) { masm.Ldr(toARMReg32(to), toMemOperand(from)); return; } vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch32 = temps.AcquireW(); masm.Ldr(scratch32, toMemOperand(from)); masm.Str(scratch32, toMemOperand(to)); } void MoveEmitterARM64::emitGeneralMove(const MoveOperand& from, const MoveOperand& to) { if (from.isGeneralReg()) { MOZ_ASSERT(to.isGeneralReg() || to.isMemory()); if (to.isGeneralReg()) masm.Mov(toARMReg64(to), toARMReg64(from)); else masm.Str(toARMReg64(from), toMemOperand(to)); return; } // {Memory OR EffectiveAddress} -> Register move. if (to.isGeneralReg()) { MOZ_ASSERT(from.isMemoryOrEffectiveAddress()); if (from.isMemory()) masm.Ldr(toARMReg64(to), toMemOperand(from)); else masm.Add(toARMReg64(to), toARMReg64(from), Operand(from.disp())); return; } vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch64 = temps.AcquireX(); // Memory -> Memory move. if (from.isMemory()) { MOZ_ASSERT(to.isMemory()); masm.Ldr(scratch64, toMemOperand(from)); masm.Str(scratch64, toMemOperand(to)); return; } // EffectiveAddress -> Memory move. MOZ_ASSERT(from.isEffectiveAddress()); MOZ_ASSERT(to.isMemory()); masm.Add(scratch64, toARMReg64(from), Operand(from.disp())); masm.Str(scratch64, toMemOperand(to)); } MemOperand MoveEmitterARM64::cycleSlot() { // Using SP as stack pointer requires alignment preservation below. MOZ_ASSERT(!masm.GetStackPointer64().Is(sp)); // emit() already allocated a slot for resolving the cycle. MOZ_ASSERT(pushedAtCycle_ != -1); return MemOperand(masm.GetStackPointer64(), masm.framePushed() - pushedAtCycle_); } void MoveEmitterARM64::breakCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type) { switch (type) { case MoveOp::FLOAT32: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch32 = temps.AcquireS(); masm.Ldr(scratch32, toMemOperand(to)); masm.Str(scratch32, cycleSlot()); } else { masm.Str(toFPReg(to, type), cycleSlot()); } break; case MoveOp::DOUBLE: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch64 = temps.AcquireD(); masm.Ldr(scratch64, toMemOperand(to)); masm.Str(scratch64, cycleSlot()); } else { masm.Str(toFPReg(to, type), cycleSlot()); } break; case MoveOp::INT32: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch32 = temps.AcquireW(); masm.Ldr(scratch32, toMemOperand(to)); masm.Str(scratch32, cycleSlot()); } else { masm.Str(toARMReg32(to), cycleSlot()); } break; case MoveOp::GENERAL: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch64 = temps.AcquireX(); masm.Ldr(scratch64, toMemOperand(to)); masm.Str(scratch64, cycleSlot()); } else { masm.Str(toARMReg64(to), cycleSlot()); } break; default: MOZ_CRASH("Unexpected move type"); } } void MoveEmitterARM64::completeCycle(const MoveOperand& from, const MoveOperand& to, MoveOp::Type type) { switch (type) { case MoveOp::FLOAT32: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch32 = temps.AcquireS(); masm.Ldr(scratch32, cycleSlot()); masm.Str(scratch32, toMemOperand(to)); } else { masm.Ldr(toFPReg(to, type), cycleSlot()); } break; case MoveOp::DOUBLE: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMFPRegister scratch = temps.AcquireD(); masm.Ldr(scratch, cycleSlot()); masm.Str(scratch, toMemOperand(to)); } else { masm.Ldr(toFPReg(to, type), cycleSlot()); } break; case MoveOp::INT32: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch32 = temps.AcquireW(); masm.Ldr(scratch32, cycleSlot()); masm.Str(scratch32, toMemOperand(to)); } else { masm.Ldr(toARMReg64(to), cycleSlot()); } break; case MoveOp::GENERAL: if (to.isMemory()) { vixl::UseScratchRegisterScope temps(&masm.asVIXL()); const ARMRegister scratch64 = temps.AcquireX(); masm.Ldr(scratch64, cycleSlot()); masm.Str(scratch64, toMemOperand(to)); } else { masm.Ldr(toARMReg64(to), cycleSlot()); } break; default: MOZ_CRASH("Unexpected move type"); } }