using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{
static class InstEmitMove
{
public static void MvnI(CodeGenContext context, uint rd, uint imm, bool immRotated, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
if (s)
{
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
if (immRotated)
{
if ((imm & (1u << 31)) != 0)
{
context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(1 << 29));
}
else
{
context.Arm64Assembler.Bfc(flagsRegister.Operand, 29, 1);
}
}
context.Arm64Assembler.Mov(rdOperand, ~imm);
context.Arm64Assembler.Tst(rdOperand, rdOperand);
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
context.SetNzcvModified();
}
else
{
context.Arm64Assembler.Mov(rdOperand, ~imm);
}
}
public static void MvnR(CodeGenContext context, uint rd, uint rm, uint sType, uint imm5, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
ScopedRegister flagsRegister = default;
if (s)
{
flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand);
}
else
{
rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType);
}
context.Arm64Assembler.Mvn(rdOperand, rmOperand);
if (s)
{
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
flagsRegister.Dispose();
context.SetNzcvModified();
}
}
public static void MvnRr(CodeGenContext context, uint rd, uint rm, uint sType, uint rs, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
ScopedRegister flagsRegister = default;
if (s)
{
flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand);
}
else
{
rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType);
}
context.Arm64Assembler.Mvn(rdOperand, rmOperand);
if (s)
{
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
flagsRegister.Dispose();
context.SetNzcvModified();
}
}
public static void MovI(CodeGenContext context, uint rd, uint imm, bool immRotated, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
if (s)
{
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
if (immRotated)
{
if ((imm & (1u << 31)) != 0)
{
context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(2));
}
else
{
context.Arm64Assembler.Bfc(flagsRegister.Operand, 1, 1);
}
}
context.Arm64Assembler.Mov(rdOperand, imm);
context.Arm64Assembler.Tst(rdOperand, rdOperand);
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
context.SetNzcvModified();
}
else
{
context.Arm64Assembler.Mov(rdOperand, imm);
}
}
public static void MovR(CodeGenContext context, uint rd, uint rm, uint sType, uint imm5, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
if (InstEmitAlu.CanShift(sType, imm5) && !s)
{
if (imm5 != 0)
{
switch ((ArmShiftType)sType)
{
case ArmShiftType.Lsl:
context.Arm64Assembler.Lsl(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Lsr:
context.Arm64Assembler.Lsr(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Asr:
context.Arm64Assembler.Asr(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Ror:
context.Arm64Assembler.Ror(rdOperand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
}
}
else
{
context.Arm64Assembler.Mov(rdOperand, rmOperand);
}
}
else
{
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
ScopedRegister flagsRegister = default;
if (s)
{
flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand);
}
else
{
rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null);
}
context.Arm64Assembler.Mov(rdOperand, rmOperand);
if (s)
{
context.Arm64Assembler.Tst(rdOperand, rdOperand);
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
flagsRegister.Dispose();
context.SetNzcvModified();
}
}
}
public static void MovR(CodeGenContext context, uint cond, uint rd, uint rm, uint sType, uint imm5, bool s)
{
if (context.ConsumeSkipNextInstruction())
{
return;
}
if ((ArmCondition)cond >= ArmCondition.Al || s)
{
MovR(context, rd, rm, sType, imm5, s);
return;
}
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
if (InstEmitAlu.CanShift(sType, imm5))
{
if (imm5 != 0)
{
switch ((ArmShiftType)sType)
{
case ArmShiftType.Lsl:
context.Arm64Assembler.Lsl(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Lsr:
context.Arm64Assembler.Lsr(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Asr:
context.Arm64Assembler.Asr(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
case ArmShiftType.Ror:
context.Arm64Assembler.Ror(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5));
break;
}
context.Arm64Assembler.Csel(rdOperand, tempRegister.Operand, rdOperand, (ArmCondition)cond);
}
else
{
Operand other = rdOperand;
InstInfo nextInstruction = context.PeekNextInstruction();
if (nextInstruction.Name == InstName.MovR)
{
// If this instruction is followed by another move with the inverse condition,
// we can just put it into the second operand of the CSEL instruction and skip the next move.
InstCondb28w4Sb20w1Rdb12w4Imm5b7w5Stypeb5w2Rmb0w4 nextInst = new(nextInstruction.Encoding);
if (nextInst.Rd == rd &&
nextInst.S == 0 &&
nextInst.Stype == 0 &&
nextInst.Imm5 == 0 &&
nextInst.Cond == (cond ^ 1u) &&
nextInst.Rm != RegisterUtils.PcRegister)
{
other = InstEmitCommon.GetInputGpr(context, nextInst.Rm);
context.SetSkipNextInstruction();
}
}
context.Arm64Assembler.Csel(rdOperand, rmOperand, other, (ArmCondition)cond);
}
}
else
{
rmOperand = InstEmitAlu.GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null);
context.Arm64Assembler.Csel(rdOperand, rmOperand, rdOperand, (ArmCondition)cond);
}
}
public static void MovRr(CodeGenContext context, uint rd, uint rm, uint sType, uint rs, bool s)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs);
if (!s)
{
InstEmitAlu.GetMShiftedByReg(context, rdOperand, rmOperand, rsOperand, sType);
}
else
{
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
rmOperand = InstEmitAlu.GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand);
context.Arm64Assembler.Mov(rdOperand, rmOperand);
context.Arm64Assembler.Tst(rdOperand, rdOperand);
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
context.SetNzcvModified();
}
}
public static void Movt(CodeGenContext context, uint rd, uint imm)
{
Operand rdOperand = InstEmitCommon.GetInputGpr(context, rd);
context.Arm64Assembler.Movk(rdOperand, (int)imm, 1);
}
public static void Pkh(CodeGenContext context, uint rd, uint rn, uint rm, bool tb, uint imm5)
{
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
if (!tb && imm5 == 0)
{
context.Arm64Assembler.Extr(rdOperand, rnOperand, rmOperand, 16);
}
else
{
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
if (tb)
{
context.Arm64Assembler.Asr(tempRegister.Operand, rmOperand, InstEmitCommon.Const(imm5 == 0 ? 31 : (int)imm5));
context.Arm64Assembler.Extr(rdOperand, tempRegister.Operand, rnOperand, 16);
}
else
{
context.Arm64Assembler.Lsl(tempRegister.Operand, rmOperand, InstEmitCommon.Const((int)imm5));
context.Arm64Assembler.Extr(rdOperand, rnOperand, tempRegister.Operand, 16);
}
}
context.Arm64Assembler.Ror(rdOperand, rdOperand, InstEmitCommon.Const(16));
}
}
}