forked from MeloNX/MeloNX
* Implement a new JIT for Arm devices * Auto-format * Make a lot of Assembler members read-only * More read-only * Fix more warnings * ObjectDisposedException.ThrowIf * New JIT cache for platforms that enforce W^X, currently unused * Remove unused using * Fix assert * Pass memory manager type around * Safe memory manager mode support + other improvements * Actual safe memory manager mode masking support * PR feedback
604 lines
26 KiB
C#
604 lines
26 KiB
C#
using Ryujinx.Cpu.LightningJit.CodeGen;
|
|
using System;
|
|
|
|
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|
{
|
|
static class InstEmitMultiply
|
|
{
|
|
public static void Mla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
context.Arm64Assembler.Madd(rdOperand, rnOperand, rmOperand, raOperand);
|
|
}
|
|
|
|
public static void Mls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
context.Arm64Assembler.Msub(rdOperand, rnOperand, rmOperand, raOperand);
|
|
}
|
|
|
|
public static void Mul(CodeGenContext context, uint rd, uint rn, uint rm, bool s)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
if (s)
|
|
{
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand);
|
|
context.Arm64Assembler.Tst(rdOperand, rdOperand);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand);
|
|
}
|
|
}
|
|
|
|
public static void Smlabb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool nHigh, bool mHigh)
|
|
{
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh);
|
|
SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh);
|
|
|
|
context.Arm64Assembler.Sxtw(tempA64, raOperand);
|
|
context.Arm64Assembler.Smaddl(tempN.Operand, tempN.Operand, tempM.Operand, tempA64);
|
|
|
|
CheckResultOverflow(context, tempM64, tempN.Operand);
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, tempN.Operand);
|
|
}
|
|
|
|
public static void Smlad(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x)
|
|
{
|
|
EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: true);
|
|
}
|
|
|
|
public static void Smlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
EmitMultiplyAddLong(context, context.Arm64Assembler.Smaddl, rdLo, rdHi, rn, rm, s);
|
|
}
|
|
|
|
public static void Smlalbb(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool nHigh, bool mHigh)
|
|
{
|
|
Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo);
|
|
Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value);
|
|
Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value);
|
|
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh);
|
|
SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh);
|
|
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand);
|
|
|
|
context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, tempA64);
|
|
|
|
if (rdLo != rdHi)
|
|
{
|
|
context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend.
|
|
}
|
|
|
|
public static void Smlald(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x)
|
|
{
|
|
EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: true);
|
|
}
|
|
|
|
public static void Smlawb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool mHigh)
|
|
{
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value);
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh);
|
|
|
|
context.Arm64Assembler.Sxtw(tempA64, raOperand);
|
|
context.Arm64Assembler.Lsl(tempA64, tempA64, InstEmitCommon.Const(16));
|
|
context.Arm64Assembler.Smaddl(tempN.Operand, rnOperand, tempM.Operand, tempA64);
|
|
context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16));
|
|
|
|
CheckResultOverflow(context, tempM64, tempN.Operand);
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, tempN.Operand);
|
|
}
|
|
|
|
public static void Smlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x)
|
|
{
|
|
EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: false);
|
|
}
|
|
|
|
public static void Smlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x)
|
|
{
|
|
EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: false);
|
|
}
|
|
|
|
public static void Smmla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r)
|
|
{
|
|
EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: true);
|
|
}
|
|
|
|
public static void Smmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r)
|
|
{
|
|
EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: false);
|
|
}
|
|
|
|
public static void Smmul(CodeGenContext context, uint rd, uint rn, uint rm, bool r)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value);
|
|
|
|
context.Arm64Assembler.Smull(rdOperand64, rnOperand, rmOperand);
|
|
|
|
if (r)
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Mov(tempRegister.Operand, 0x80000000u);
|
|
context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempRegister.Operand);
|
|
}
|
|
|
|
context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
public static void Smuad(CodeGenContext context, uint rd, uint rn, uint rm, bool x)
|
|
{
|
|
EmitSmuadSmusd(context, rd, rn, rm, x, add: true);
|
|
}
|
|
|
|
public static void Smulbb(CodeGenContext context, uint rd, uint rn, uint rm, bool nHigh, bool mHigh)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value);
|
|
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh);
|
|
SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh);
|
|
|
|
context.Arm64Assembler.Smull(rdOperand64, tempN.Operand, tempM.Operand);
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend.
|
|
}
|
|
|
|
public static void Smull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
EmitMultiplyLong(context, context.Arm64Assembler.Smull, rdLo, rdHi, rn, rm, s);
|
|
}
|
|
|
|
public static void Smulwb(CodeGenContext context, uint rd, uint rn, uint rm, bool mHigh)
|
|
{
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value);
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh);
|
|
|
|
context.Arm64Assembler.Smull(tempN.Operand, rnOperand, tempM.Operand);
|
|
context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16));
|
|
|
|
CheckResultOverflow(context, tempM64, tempN.Operand);
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, tempN.Operand);
|
|
}
|
|
|
|
public static void Smusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x)
|
|
{
|
|
EmitSmuadSmusd(context, rd, rn, rm, x, add: false);
|
|
}
|
|
|
|
public static void Umaal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm)
|
|
{
|
|
Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo);
|
|
Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value);
|
|
Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value);
|
|
|
|
if (rdLo == rdHi)
|
|
{
|
|
using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempRegister64 = new(OperandKind.Register, OperandType.I64, tempRegister.Operand.Value);
|
|
|
|
context.Arm64Assembler.Umaddl(tempRegister64, rnOperand, rmOperand, rdLoOperand64);
|
|
context.Arm64Assembler.Add(rdLoOperand64, tempRegister64, rdHiOperand64);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Umaddl(rdLoOperand64, rnOperand, rmOperand, rdLoOperand64);
|
|
context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, rdHiOperand64);
|
|
}
|
|
|
|
if (rdLo != rdHi)
|
|
{
|
|
context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend.
|
|
}
|
|
|
|
public static void Umlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
EmitMultiplyAddLong(context, context.Arm64Assembler.Umaddl, rdLo, rdHi, rn, rm, s);
|
|
}
|
|
|
|
public static void Umull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
EmitMultiplyLong(context, context.Arm64Assembler.Umull, rdLo, rdHi, rn, rm, s);
|
|
}
|
|
|
|
private static void EmitMultiplyLong(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo);
|
|
Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value);
|
|
Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value);
|
|
|
|
if (s)
|
|
{
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
action(rdLoOperand64, rnOperand, rmOperand);
|
|
context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
}
|
|
else
|
|
{
|
|
action(rdLoOperand64, rnOperand, rmOperand);
|
|
}
|
|
|
|
if (rdLo != rdHi)
|
|
{
|
|
context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend.
|
|
}
|
|
|
|
private static void EmitMultiplyAddLong(CodeGenContext context, Action<Operand, Operand, Operand, Operand> action, uint rdLo, uint rdHi, uint rn, uint rm, bool s)
|
|
{
|
|
Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo);
|
|
Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value);
|
|
Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value);
|
|
|
|
using ScopedRegister raRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raRegister.Operand.Value);
|
|
|
|
context.Arm64Assembler.Lsl(raOperand64, rdHiOperand64, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.Orr(raOperand64, raOperand64, rdLoOperand);
|
|
|
|
if (s)
|
|
{
|
|
using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand);
|
|
|
|
action(rdLoOperand64, rnOperand, rmOperand, raOperand64);
|
|
context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64);
|
|
|
|
InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand);
|
|
|
|
context.SetNzcvModified();
|
|
}
|
|
else
|
|
{
|
|
action(rdLoOperand64, rnOperand, rmOperand, raOperand64);
|
|
}
|
|
|
|
if (rdLo != rdHi)
|
|
{
|
|
context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend.
|
|
}
|
|
|
|
private static void EmitSmladSmlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x, bool add)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value);
|
|
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value);
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
ScopedRegister swapTemp = default;
|
|
|
|
if (x)
|
|
{
|
|
swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
rmOperand = swapTemp.Operand;
|
|
}
|
|
|
|
context.Arm64Assembler.Sxth(tempN64, rnOperand);
|
|
context.Arm64Assembler.Sxth(tempM64, rmOperand);
|
|
context.Arm64Assembler.Sxtw(tempA64, raOperand);
|
|
|
|
context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64);
|
|
|
|
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16));
|
|
context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
if (add)
|
|
{
|
|
context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64);
|
|
}
|
|
|
|
context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64);
|
|
|
|
CheckResultOverflow(context, tempM64, rdOperand64);
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend.
|
|
|
|
if (x)
|
|
{
|
|
swapTemp.Dispose();
|
|
}
|
|
}
|
|
|
|
private static void EmitSmlaldSmlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x, bool add)
|
|
{
|
|
Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo);
|
|
Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value);
|
|
Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value);
|
|
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value);
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
ScopedRegister swapTemp = default;
|
|
|
|
if (x)
|
|
{
|
|
swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
rmOperand = swapTemp.Operand;
|
|
}
|
|
|
|
context.Arm64Assembler.Sxth(tempN64, rnOperand);
|
|
context.Arm64Assembler.Sxth(tempM64, rmOperand);
|
|
|
|
context.Arm64Assembler.Mul(rdLoOperand64, tempN64, tempM64);
|
|
|
|
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16));
|
|
context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
if (add)
|
|
{
|
|
context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Smsubl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64);
|
|
}
|
|
|
|
context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32));
|
|
context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand);
|
|
|
|
context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, tempA64);
|
|
|
|
if (rdLo != rdHi)
|
|
{
|
|
context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend.
|
|
|
|
if (x)
|
|
{
|
|
swapTemp.Dispose();
|
|
}
|
|
}
|
|
|
|
private static void EmitSmmlaSmmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r, bool add)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
Operand raOperand = InstEmitCommon.GetInputGpr(context, ra);
|
|
|
|
Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value);
|
|
Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raOperand.Value);
|
|
|
|
using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value);
|
|
|
|
context.Arm64Assembler.Lsl(tempA64, raOperand64, InstEmitCommon.Const(32));
|
|
|
|
if (add)
|
|
{
|
|
context.Arm64Assembler.Smaddl(rdOperand64, rnOperand, rmOperand, tempA64);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Smsubl(rdOperand64, rnOperand, rmOperand, tempA64);
|
|
}
|
|
|
|
if (r)
|
|
{
|
|
context.Arm64Assembler.Mov(tempA.Operand, 0x80000000u);
|
|
context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64);
|
|
}
|
|
|
|
context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32));
|
|
}
|
|
|
|
private static void EmitSmuadSmusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x, bool add)
|
|
{
|
|
Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd);
|
|
Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn);
|
|
Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm);
|
|
|
|
Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value);
|
|
|
|
using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value);
|
|
Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value);
|
|
|
|
ScopedRegister swapTemp = default;
|
|
|
|
if (x)
|
|
{
|
|
swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped();
|
|
|
|
context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
rmOperand = swapTemp.Operand;
|
|
}
|
|
|
|
context.Arm64Assembler.Sxth(tempN64, rnOperand);
|
|
context.Arm64Assembler.Sxth(tempM64, rmOperand);
|
|
|
|
context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64);
|
|
|
|
context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16));
|
|
context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16));
|
|
|
|
if (add)
|
|
{
|
|
context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64);
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64);
|
|
}
|
|
|
|
context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend.
|
|
|
|
if (x)
|
|
{
|
|
swapTemp.Dispose();
|
|
}
|
|
}
|
|
|
|
private static void SelectSignedHalfword(CodeGenContext context, Operand dest, Operand source, bool high)
|
|
{
|
|
if (high)
|
|
{
|
|
context.Arm64Assembler.Asr(dest, source, InstEmitCommon.Const(16));
|
|
}
|
|
else
|
|
{
|
|
context.Arm64Assembler.Sxth(dest, source);
|
|
}
|
|
}
|
|
|
|
private static void CheckResultOverflow(CodeGenContext context, Operand temp64, Operand result)
|
|
{
|
|
context.Arm64Assembler.Sxtw(temp64, result);
|
|
context.Arm64Assembler.Sub(temp64, temp64, result);
|
|
|
|
int branchIndex = context.CodeWriter.InstructionPointer;
|
|
|
|
context.Arm64Assembler.Cbz(temp64, 0);
|
|
|
|
// Set Q flag if we had an overflow.
|
|
InstEmitSaturate.SetQFlag(context);
|
|
|
|
int delta = context.CodeWriter.InstructionPointer - branchIndex;
|
|
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
|
|
}
|
|
}
|
|
}
|